1 Introduction

This package provides a set of methods for metabolomics data analysis, including data loading in different formats, pre-processing, metabolite identification, univariate and multivariate data analysis, machine learning, feature selection and pathway analysis.

A web version, WebSpecmine, is now available, in case you do not have much skills in the R environment or programming, with a easy-to-use interface, with the features implemented in this package and a public repository with metabolomics data (you can also save your own data, private or not, if you create an account). The specmine R package may be better to use, if wanted a more flexible use of the provided features and use it with other R tools.

2 Package instalation

So that the package can be fully used and installed correctly, some R packages have to be installed before installing specmine

2.1 Installing necessary CRAN packages

2.1.1 Necessary packages that may not be installed

install.packages(c('lattice', 'ggplot2', 'caret', 'BradleyTerry2', 'e1071', 'earth',
                   'fastICA','gam', 'ipred', 'kernlab', 'klaR', 'MASS', 'ellipse', 'mda',
                   'mgcv', 'mlbench','MLmetrics', 'nnet', 'party', 'pls', 'pROC', 'proxy',
                   'randomForest', 'RANN','spls', 'subselect', 'pamr', 'superpc', 'Cubist',
                   'testthat', 'igraph','Rweka', 'stats', 'scatterplot3d', 'compare',
                   'hyperSpec', 'ChemoSpec', 'baseline','rgl', 'Metrics', 'GGally',
                   'ggdendro', 'pcaPP', 'RColorBrewer', 'grid', 'methods', 'qdap',
                   'shinydashboard', 'shinyBS', 'shinyjs', 'DT', 'RMySQL', 'bcrypt',
                   'openssl', 'GGally','shinyWidgets', 'colourpicker', 'devtools',
                   'MLmetrics', 'speaq'))

2.2 Installing necessary BioConductor packages

1: If bioconductor is not yet installed:

source("https://bioconductor.org/biocLite.R")
biocLite()

2: Bioconductor packages necessary:

source("https://bioconductor.org/biocLite.R")
biocLite("impute", "genefilter", "xcms", "MAIT", "KEGGREST", "KEGGgraph", "mzR")

2.3 Installing other packages

devtools::install_github('cytoscape/r-cytoscape.js')

2.4 Installing specmine

2.4.1 CRAN version

install.packages("specmine")

2.4.2 Development version

Note that, at some point, the development version may not be conpletely error free.

devtools::install_bitbucket('chrisbcl/metabolomicspackage', ref='master')

3 Supported Data

Various types of data are supported, in many formats. The website considers that each data file represents one distinct sample, with exception for when one csv file of UV-VIS, IR and Raman Spectra is given and for the data file of concentrations data.

3.1 NMR and GC/LC-MS Peak Lists

The peak lists data files must have the CSV format. Each CSV file must represent a sample and have two columns: the first one corresponds to the chemical shifts (in ppms) or the mass/charge ratios and the second one the intensities of those peaks.

Part of a CSV file example of a peak list:

                                          ppm,intensity
                                          0.74,0.0001
                                          0.89,0.0004
                                          0.90,0.0007
                                          0.91,0.0005
                                          0.91,0.0008
                                          0.92,0.0004
                                          0.94,0.0003
                                          0.95,0.0004
                                          0.96,0.0009

3.2 NMR Spectra

There are two nmr spectra formats that are supported.

The BRUKER format is supported, if the processed spectra are given. Each spectrum data has to be in a different folder. Each folder has to have the following structure:

At least the files procs and 1r have to be present. They have to be inside spectrumfoldername/pdata/1

The VARIAN format is supported, only if the raw fid file is given, alongside with the procpar file. Each spectrum data has to be in a different folder. Each folder has to have the following structure:

3.3 GC/LC-MS Spectra

The MS spectral data files must either have .mzXML, .netCDF or mzData formats.

3.4 UV-Vis, IR and Raman Spectra

The data files of these type of spectra must be in one of the following formats: CSV, (J)DX, SPC or MS EXCEL (.xlsx).

For data in MS EXCEL or CSV files, each file must have two columns: the first one representing the wavenumber, wavelength or raman shift, according to the type of spectra, and the second one the value of the measurements.

When only one CSV file is given, the structure as to be similar to the following example (the first column corresponds to the wavenumber, wavelength or raman shift, according to the type of spectra):

                                  ,sampleName1,sampleName2
                                  200,0.085956648,0.04830468
                                  201,0.067182627,0.017316359
                                  202,0.044842223,0.026930633
                                  203,0.051335963,0.041539431

3.5 Concentrations Data

Concentrations data must be a CSV or TSV file with the samples names in the first column (each line then corresponds to a sample) and the concentrations values for each metabolite in the following columns. Alternatively, samples names can be in the first line (each column then corresponds to a sample) and the concentrations values for each metabolite in the following lines.

Part of a CSV example file of concentrations file:

                Patient ID,1.6-Anhydro-beta-D-glucose,1-Methylnicotinamide,2-Aminobutyrate
                PIF_178,40.85,65.37,18.73
                PIF_087,62.18,340.36,24.29
                PIF_090,270.43,64.72,12.18
                NETL_005_V1,154.47,52.98,172.43
                PIF_115,22.2,73.7,15.64

3.6 Metadata File

As regards to the metadata file, it can either have CSV or TSV format. Each line should correspond to a sample, where the first column represents the names of such samples, and the remaining ones the metadata classes.

The first column corresponds to the names of the samples. For the cases where more than one data file is given, the names of the samples have to correspond to the names of the data files.

Here you have an example of a metadata file:

                                  Sample Name,Seasons
                                  July2010,Winter
                                  September2010,Spring
                                  October2010,Spring
                                  November2010,Spring
                                  February2011,Sum/Aut
                                  March2011,Sum/Aut
                                  April2011,Sum/Aut
                                  may2011,Sum/Aut
                                  June2011,Winter
                                  July2011,Winter
                                  August2011,Winter
                                  September2011,Spring
                                  October2011,Spring

4 Read data into specmine

The data files used in the examples decribed in this section can be obtained in here.

4.1 Structure of a specmine dataset

A specmine dataset is a list with the following elements:

  • data: data frame of the data points. Each column corresponds to a samples and each line to a data variable. The values in each cell are the yy values of said variable in said sample;

  • metadata: data frame of the metadata information. Each column corresponds to a metadata variable and each line to a samples. The values in each cell are the values of said variable in said sample;

  • type: string indicating the type of data. It can either be “nmr-peaks”, “nmr-spectra”, “lcms-peaks”, “gcms-peaks”, “lcms-spectra”, “gcms-spectra”, “ir-spectra”, “uvv-spectra”, “raman-spectra”, “integrated-data”, “concentrations”, “undefined”;

  • description: a short description of the data;

  • labels: list with the following elements

    • x: xx axis labels;

    • val: yy axis labels.

4.2 NMR and GC/LC-MS Peak Lists (including peak alignment)

4.2.1 Functions to use

1: Read NMR and MS Peak lists data into list of data samples

read_csvs_folder(foldername, header=TRUE, sep=“,”, dec=“.”, …)

  • foldername: string containing the path of the data folder;

  • header: boolean value (TRUE or FALSE) indicating whether data files have a header row with the names of the data variables. Defaults to TRUE;

  • sep: the separator character of the data values. Defaults to “,”;

  • dec: character used in the file for decimal points. Defaults to “.”;

  • : additional parameters for read.csv function from utils package.

2: Read metadata file (optional step but recomended)

read_metadata(filename, header.col = T, header.row = T, sep = “,”)

  • filename : string indicating the path of the file with the metadata;

  • header.col: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep: the separator character. Defaults to “,”.

3: Perform Peak Alignment, into specmine dataset

group_peaks(sample.list, type, method = “own”, metadata = NULL, samp.classes = 1, description = “”, label.x = NULL, label.values = NULL, step = 0.03)

  • sample.list: list containing the sample’s data. This list can be obtained from the function read_csvs_folder above;

  • type: type of the data. Can either be “nmr-peaks”, “lcms-peaks” or “gcms-peaks”;

  • method: method of peak alignment. Can either be

    • “own”: Specmine method. Default value;

    • “metaboanalyst”: MetaboAnalyst method, which is for using the peak alignment used in MetaboAnalyst software.

  • metadata: data frame containing the metadata. Can be obtained from the function read_metadata above; optional but recomended

  • samp.classes: string containg the metadata’s variable name to be used in the MetaboAnalyst method. Can be obtained from colnames(metadata_dataframe). Defaults to the variable represented by the first column.

  • description: short description of the data. optional

  • label.x: the label for the x values. optional

  • label.values: the label for the y values. optional

  • step: step value for the peak alignment process in the specmine method. Defaults to 0.03.

4.2.2 Example

1. Strings indicating where data and metadata is:

nmr_peaks_lists_data_folder="/home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data"
nmr_peaks_lists_metadata_file="/home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/propolis_nmr_metadata.csv"

2. Read data folder:

nmr_peaks_list=read_csvs_folder(nmr_peaks_lists_data_folder)
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AC_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AC_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AC_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AC_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AN_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AN_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AN_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/AN_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/BR_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/BR_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/BR_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/BR_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CE_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CE_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CE_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CE_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CN_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CN_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CN_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/CN_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/DC_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/DC_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/DC_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/DC_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/FP_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/FP_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/FP_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/IT_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/IT_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/IT_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/JB_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/JB_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/JB_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/JB_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/PU_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/PU_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/PU_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/PU_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SA_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SA_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SA_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SA_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJ_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJC_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJC_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJC_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJ_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJ_sp.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/SJ_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/UR_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/UR_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/UR_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/UR_wi.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/VR_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/VR_sm.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/VR_sp.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/XX_au.csv"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/XX_sm.CSV"
## [1] "Reading sample  /home/scardoso/Documents/metabolomics_datasets/NMR/Peak lists/propolis/Propolis_NMR Data_data/XX_wi.csv"

3. Read metadata file:

nmr_peaks_metadata=read_metadata(nmr_peaks_lists_metadata_file)

4. Align peaks using specmine method with a step of 0.03 (default values, so it is not necessary to define them), which will now return the specmine dataset:

nmr_peaks_dataset=group_peaks(nmr_peaks_list, "nmr-peaks", metadata = nmr_peaks_metadata, description="propolis nmr samples", label.x = "ppm", label.values = "intensity")

5. Table of the data just loaded (it is possible to realize that there are missing values):

DT::datatable(nmr_peaks_dataset$data, options=list(scrollX = TRUE))

6. Table of the metadata just loaded:

DT::datatable(nmr_peaks_dataset$metadata)

4.3 NMR Spectra (peak detection independent of data reading)

4.3.1 Functions to use

4.3.1.1 When the data is in BRUKER format

read_Bruker_files(bruker_directory, metadata_file=NULL, m.header_col=T, m.header_row=T, m.sep=“,”, samples.names=NULL, zipped=T, description=“”, label.x=“ppm”, label.values=“intensity”)

  • bruker_directory: string containing the path of the data folder with all the spectra folders;

  • metadata_file: string containing the path of the metadata file; optional but recomended

  • m.header_col: boolean value (TRUE or FALSE) indicating if the metadata file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • m.header_row: boolean value (TRUE or FALSE) indicating if the metadata file contains a header row with the name of the samples. Defaults to TRUE;

  • m.sep: the separator character of the metadata file. Defaults to “,”;

  • samples.names: CSV file where the first column represents the samples names and in the second column the names of the spectra directories to which they correspond. If NULL, it will be considered that the directories names are the samples names (it has to be the same names that appear in the metadata file);

  • zipped: Boleean value (TRUE or FALSE) indicating if the spectra directories are zipped (.zip) or not. The compressed files must have the extension .zip. If not, you will have to uncompress them yourself.

  • description: a short text describing the dataset. optional

  • label.x: the label for the x values. optional

  • label.values: the label for the y values. optional

4.3.1.2 When the data is VARIAN format

TO USE THIS FUNCTION YOU MUST HAVE PYTHON3 INSTALLED, WITH THE MODULE nmrglue INSTALLED

read_varian_spectra_raw(varian_spectra_directory, metadata_file=NULL, m.header_col=T, m.header_row=T, m.sep=“,”, samples.names=NULL, zero_filling=T, apodization=T, zipped=T, description=“”, label.x=“ppm”, label.values=“intensity”)

  • varian_spectra_directory: string containing the path of the data folder with all the spectra folders;

  • metadata_file: string containing the path of the metadata file; optional but recomended

  • m.header_col: boolean value (TRUE or FALSE) indicating if the metadata file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • m.header_row: boolean value (TRUE or FALSE) indicating if the metadata file contains a header row with the name of the samples. Defaults to TRUE;

  • m.sep: the separator character of the metadata file. Defaults to “,”;

  • samples.names: CSV file where the first column represents the samples names and in the second column the names of the spectra directories to which they correspond. If NULL, it will be considered that the directories names are the samples names (it has to be the same names that appear in the metadata file);

  • zero_filling: boolean value (TRUE or FALSE) indicating whether zero-filling should be performed or not when processing the fid spectra. Defaults to TRUE;

  • apodization: boolean value (TRUE or FALSE) indicating whether apodization should be performed or not when processing the fid spectra. Defaults to TRUE;

  • zipped: Boleean value (TRUE or FALSE) indicating if the spectra directories are zipped (.zip) or not. The compressed files must have the extension .zip. If not, you will have to uncompress them yourself.

  • description: a short text describing the dataset. optional

  • label.x: the label for the x values. optional

  • label.values: the label for the y values. optional

4.3.1.3 After reading spectra, detection and alignment of peaks can be performed, as it is not obligatory when reading the data, which happens when reading MS spectra

Detection of peaks, followed by alignment of those peaks

detect_nmr_peaks_from_dataset(dataset, baseline_tresh=50000, ap.method=“own”, ap.samp.classes=1, ap.step=0.03)

  • dataset: a specmine dataset of type nmr-spectra;

  • baseline_tresh: Minimum intensity value that peaks must have. Peaks with intensity smaller than baseline_tresh will not be considered as detected peaks. Defaults to 50000;

  • ap.method: method of peak alignment. Can either be

    • “own”: Specmine method. Default value;

    • “metaboanalyst”: MetaboAnalyst method, which is for using the peak alignment used in MetaboAnalyst software.

  • ap.samp.classes: string containg the metadata’s variable name to be used in the MetaboAnalyst method. Can be obtained from colnames(metadata_dataframe). Defaults to the variable represented by the first column.

  • ap.step: step value for the peak alignment process in the specmine method. Defaults to 0.03.

4.3.2 Examples

4.3.2.1 BRUKER data

The example data here used was obtained from the Metabolights database, under the ID MTBLS151. The data was obtained using the get_metabolights_study function.

1. Strings indicating where data and metadata is:

bruker_nmr_spectra_folder="/home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS151"
bruker_nmr_metadata_file="/home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS151/metadata.csv"

2. Loading NMR bruker spectral data to specmine:

In this data, the spectra folders’ names correspond to the names of the samples, so no file will be given to the argument samples.names. These folders are zipped (.zip)

nmr_bruker_spectra_dataset=read_Bruker_files(bruker_nmr_spectra_folder, metadata_file=bruker_nmr_metadata_file)
## Reading Metadata file
## Reading sample  L-IL308C1-1  in  /home/scardoso/temp/L-IL308C1-1/L-IL308C1-1/10/pdata/1
## Reading sample  L-IL308C1-2  in  /home/scardoso/temp/L-IL308C1-2/L-IL308C1-2/10/pdata/1
## Reading sample  L-IL308C1-3  in  /home/scardoso/temp/L-IL308C1-3/L-IL308C1-3/10/pdata/1
## Reading sample  L-IL308C2-1  in  /home/scardoso/temp/L-IL308C2-1/L-IL308C2-1/10/pdata/1
## Reading sample  L-IL308C2-2  in  /home/scardoso/temp/L-IL308C2-2/L-IL308C2-2/10/pdata/1
## Reading sample  L-IL308C2-3  in  /home/scardoso/temp/L-IL308C2-3/L-IL308C2-3/10/pdata/1
## Reading sample  L-IL308C3-1  in  /home/scardoso/temp/L-IL308C3-1/L-IL308C3-1/10/pdata/1
## Reading sample  L-IL308C3-2  in  /home/scardoso/temp/L-IL308C3-2/L-IL308C3-2/10/pdata/1
## Reading sample  L-IL308C3-3  in  /home/scardoso/temp/L-IL308C3-3/L-IL308C3-3/10/pdata/1
## Reading sample  L-IL308C4-1  in  /home/scardoso/temp/L-IL308C4-1/L-IL308C4-1/10/pdata/1
## Reading sample  L-IL308C4-2  in  /home/scardoso/temp/L-IL308C4-2/L-IL308C4-2/10/pdata/1
## Reading sample  L-IL308C4-3  in  /home/scardoso/temp/L-IL308C4-3/L-IL308C4-3/10/pdata/1
## Reading sample  L-IL308C8-1  in  /home/scardoso/temp/L-IL308C8-1/L-IL308C8-1/10/pdata/1
## Reading sample  L-IL308C8-2  in  /home/scardoso/temp/L-IL308C8-2/L-IL308C8-2/10/pdata/1
## Reading sample  L-IL308C8-3  in  /home/scardoso/temp/L-IL308C8-3/L-IL308C8-3/10/pdata/1
## Reading sample  L-IL308T1-1  in  /home/scardoso/temp/L-IL308T1-1/L-IL308T1-1/10/pdata/1
## Reading sample  L-IL308T1-2  in  /home/scardoso/temp/L-IL308T1-2/L-IL308T1-2/10/pdata/1
## Reading sample  L-IL308T1-3  in  /home/scardoso/temp/L-IL308T1-3/L-IL308T1-3/10/pdata/1
## Reading sample  L-IL308T2-1  in  /home/scardoso/temp/L-IL308T2-1/L-IL308T2-1/10/pdata/1
## Reading sample  L-IL308T2-2  in  /home/scardoso/temp/L-IL308T2-2/L-IL308T2-2/10/pdata/1
## Reading sample  L-IL308T2-3  in  /home/scardoso/temp/L-IL308T2-3/L-IL308T2-3/10/pdata/1
## Reading sample  L-IL308T3-1  in  /home/scardoso/temp/L-IL308T3-1/L-IL308T3-1/10/pdata/1
## Reading sample  L-IL308T3-2  in  /home/scardoso/temp/L-IL308T3-2/L-IL308T3-2/10/pdata/1
## Reading sample  L-IL308T3-3  in  /home/scardoso/temp/L-IL308T3-3/L-IL308T3-3/10/pdata/1
## Reading sample  L-IL308T4-1  in  /home/scardoso/temp/L-IL308T4-1/L-IL308T4-1/10/pdata/1
## Reading sample  L-IL308T4-2  in  /home/scardoso/temp/L-IL308T4-2/L-IL308T4-2/10/pdata/1
## Reading sample  L-IL308T4-3  in  /home/scardoso/temp/L-IL308T4-3/L-IL308T4-3/10/pdata/1
## Reading sample  L-IL308T8-1  in  /home/scardoso/temp/L-IL308T8-1/L-IL308T8-1/10/pdata/1
## Reading sample  L-IL308T8-2  in  /home/scardoso/temp/L-IL308T8-2/L-IL308T8-2/10/pdata/1
## Reading sample  L-IL308T8-3  in  /home/scardoso/temp/L-IL308T8-3/L-IL308T8-3/10/pdata/1
## Reading sample  L-IL7C1-1  in  /home/scardoso/temp/L-IL7C1-1/L-IL7C1-1/10/pdata/1
## Reading sample  L-IL7C1-2  in  /home/scardoso/temp/L-IL7C1-2/L-IL7C1-2/10/pdata/1
## Reading sample  L-IL7C1-3  in  /home/scardoso/temp/L-IL7C1-3/L-IL7C1-3/10/pdata/1
## Reading sample  L-IL7C2-1  in  /home/scardoso/temp/L-IL7C2-1/L-IL7C2-1/10/pdata/1
## Reading sample  L-IL7C2-2  in  /home/scardoso/temp/L-IL7C2-2/L-IL7C2-2/10/pdata/1
## Reading sample  L-IL7C2-3  in  /home/scardoso/temp/L-IL7C2-3/L-IL7C2-3/10/pdata/1
## Reading sample  L-IL7C3-1  in  /home/scardoso/temp/L-IL7C3-1/L-IL7C3-1/10/pdata/1
## Reading sample  L-IL7C3-2  in  /home/scardoso/temp/L-IL7C3-2/L-IL7C3-2/10/pdata/1
## Reading sample  L-IL7C3-3  in  /home/scardoso/temp/L-IL7C3-3/L-IL7C3-3/10/pdata/1
## Reading sample  L-IL7C4-1  in  /home/scardoso/temp/L-IL7C4-1/L-IL7C4-1/10/pdata/1
## Reading sample  L-IL7C4-2  in  /home/scardoso/temp/L-IL7C4-2/L-IL7C4-2/10/pdata/1
## Reading sample  L-IL7C4-3  in  /home/scardoso/temp/L-IL7C4-3/L-IL7C4-3/11/pdata/1
## Reading sample  L-IL7C8-1  in  /home/scardoso/temp/L-IL7C8-1/L-IL7C8-1/10/pdata/1
## Reading sample  L-IL7C8-2  in  /home/scardoso/temp/L-IL7C8-2/L-IL7C8-2/10/pdata/1
## Reading sample  L-IL7C8-3  in  /home/scardoso/temp/L-IL7C8-3/L-IL7C8-3/10/pdata/1
## Reading sample  L-IL7T1-1  in  /home/scardoso/temp/L-IL7T1-1/L-IL7T1-1/10/pdata/1
## Reading sample  L-IL7T1-2  in  /home/scardoso/temp/L-IL7T1-2/L-IL7T1-2/10/pdata/1
## Reading sample  L-IL7T1-3  in  /home/scardoso/temp/L-IL7T1-3/L-IL7T1-3/10/pdata/1
## Reading sample  L-IL7T2-1  in  /home/scardoso/temp/L-IL7T2-1/L-IL7T2-1/10/pdata/1
## Reading sample  L-IL7T2-2  in  /home/scardoso/temp/L-IL7T2-2/L-IL7T2-2/10/pdata/1
## Reading sample  L-IL7T2-3  in  /home/scardoso/temp/L-IL7T2-3/L-IL7T2-3/10/pdata/1
## Reading sample  L-IL7T3-1  in  /home/scardoso/temp/L-IL7T3-1/L-IL7T3-1/10/pdata/1
## Reading sample  L-IL7T3-1  in  /home/scardoso/temp/L-IL7T3-1/L-IL7T3-1/20/pdata/1
## Reading sample  L-IL7T3-2  in  /home/scardoso/temp/L-IL7T3-2/L-IL7T3-2/10/pdata/1
## Reading sample  L-IL7T3-3  in  /home/scardoso/temp/L-IL7T3-3/L-IL7T3-3/10/pdata/1
## Reading sample  L-IL7T4-1  in  /home/scardoso/temp/L-IL7T4-1/L-IL7T4-1/10/pdata/1
## Reading sample  L-IL7T4-2  in  /home/scardoso/temp/L-IL7T4-2/L-IL7T4-2/10/pdata/1
## Reading sample  L-IL7T4-3  in  /home/scardoso/temp/L-IL7T4-3/L-IL7T4-3/10/pdata/1
## Reading sample  L-IL7T8-1  in  /home/scardoso/temp/L-IL7T8-1/L-IL7T8-1/10/pdata/1
## Reading sample  L-IL7T8-2  in  /home/scardoso/temp/L-IL7T8-2/L-IL7T8-2/10/pdata/1
## Reading sample  L-IL7T8-3  in  /home/scardoso/temp/L-IL7T8-3/L-IL7T8-3/10/pdata/1
## Reading sample  L-KDC1-1  in  /home/scardoso/temp/L-KDC1-1/L-KDC1-1/10/pdata/1
## Reading sample  L-KDC1-2  in  /home/scardoso/temp/L-KDC1-2/L-KDC1-2/10/pdata/1
## Reading sample  L-KDC1-3  in  /home/scardoso/temp/L-KDC1-3/L-KDC1-3/10/pdata/1
## Reading sample  L-KDC2-1  in  /home/scardoso/temp/L-KDC2-1/L-KDC2-1/10/pdata/1
## Reading sample  L-KDC2-2  in  /home/scardoso/temp/L-KDC2-2/L-KDC2-2/10/pdata/1
## Reading sample  L-KDC2-3  in  /home/scardoso/temp/L-KDC2-3/L-KDC2-3/10/pdata/1
## Reading sample  L-KDC3-1  in  /home/scardoso/temp/L-KDC3-1/L-KDC3-1/10/pdata/1
## Reading sample  L-KDC3-2  in  /home/scardoso/temp/L-KDC3-2/L-KDC3-2/10/pdata/1
## Reading sample  L-KDC3-3  in  /home/scardoso/temp/L-KDC3-3/L-KDC3-3/10/pdata/1
## Reading sample  L-KDC4-1  in  /home/scardoso/temp/L-KDC4-1/L-KDC4-1/10/pdata/1
## Reading sample  L-KDC4-2  in  /home/scardoso/temp/L-KDC4-2/L-KDC4-2/10/pdata/1
## Bruker file does not exist in datapath, or other problems with bruker files...
## Reading sample  L-KDC4-2  in  /home/scardoso/temp/L-KDC4-2/L-KDC4-2/20/pdata/1
## Reading sample  L-KDC4-3  in  /home/scardoso/temp/L-KDC4-3/L-KDC4-3/10/pdata/1
## Reading sample  L-KDC8-1  in  /home/scardoso/temp/L-KDC8-1/L-KDC8-1/10/pdata/1
## Reading sample  L-KDC8-2  in  /home/scardoso/temp/L-KDC8-2/L-KDC8-2/10/pdata/1
## Reading sample  L-KDC8-3  in  /home/scardoso/temp/L-KDC8-3/L-KDC8-3/10/pdata/1
## Reading sample  L-KDT1-1  in  /home/scardoso/temp/L-KDT1-1/L-KDT1-1/10/pdata/1
## Reading sample  L-KDT1-2  in  /home/scardoso/temp/L-KDT1-2/L-KDT1-2/10/pdata/1
## Reading sample  L-KDT1-3  in  /home/scardoso/temp/L-KDT1-3/L-KDT1-3/10/pdata/1
## Reading sample  L-KDT2-1  in  /home/scardoso/temp/L-KDT2-1/L-KDT2-1/10/pdata/1
## Reading sample  L-KDT2-2  in  /home/scardoso/temp/L-KDT2-2/L-KDT2-2/10/pdata/1
## Reading sample  L-KDT2-3  in  /home/scardoso/temp/L-KDT2-3/L-KDT2-3/10/pdata/1
## Reading sample  L-KDT3-1  in  /home/scardoso/temp/L-KDT3-1/L-KDT3-1/10/pdata/1
## Reading sample  L-KDT3-2  in  /home/scardoso/temp/L-KDT3-2/L-KDT3-2/10/pdata/1
## Reading sample  L-KDT3-3  in  /home/scardoso/temp/L-KDT3-3/L-KDT3-3/10/pdata/1
## Reading sample  L-KDT4-1  in  /home/scardoso/temp/L-KDT4-1/L-KDT4-1/10/pdata/1
## Reading sample  L-KDT4-2  in  /home/scardoso/temp/L-KDT4-2/L-KDT4-2/10/pdata/1
## Reading sample  L-KDT4-3  in  /home/scardoso/temp/L-KDT4-3/L-KDT4-3/10/pdata/1
## Reading sample  L-KDT8-1  in  /home/scardoso/temp/L-KDT8-1/L-KDT8-1/10/pdata/1
## Reading sample  L-KDT8-2  in  /home/scardoso/temp/L-KDT8-2/L-KDT8-2/10/pdata/1
## Reading sample  L-KDT8-3  in  /home/scardoso/temp/L-KDT8-3/L-KDT8-3/10/pdata/1
## Reading sample  R-IL308C1  in  /home/scardoso/temp/R-IL308C1/R-IL308C1/10/pdata/1
## Reading sample  R-IL308C2  in  /home/scardoso/temp/R-IL308C2/R-IL308C2/10/pdata/1
## Reading sample  R-IL308C3  in  /home/scardoso/temp/R-IL308C3/R-IL308C3/10/pdata/1
## Reading sample  R-IL308C4  in  /home/scardoso/temp/R-IL308C4/R-IL308C4/10/pdata/1
## Reading sample  R-IL308C8  in  /home/scardoso/temp/R-IL308C8/R-IL308C8/10/pdata/1
## Reading sample  R-IL308T1  in  /home/scardoso/temp/R-IL308T1/R-IL308T1/10/pdata/1
## Reading sample  R-IL308T2  in  /home/scardoso/temp/R-IL308T2/R-IL308T2/10/pdata/1
## Reading sample  R-IL308T3  in  /home/scardoso/temp/R-IL308T3/R-IL308T3/10/pdata/1
## Reading sample  R-IL308T4  in  /home/scardoso/temp/R-IL308T4/R-IL308T4/10/pdata/1
## Reading sample  R-IL308T8  in  /home/scardoso/temp/R-IL308T8/R-IL308T8/10/pdata/1
## Reading sample  R-IL7C1  in  /home/scardoso/temp/R-IL7C1/R-IL7C1/10/pdata/1
## Reading sample  R-IL7C2  in  /home/scardoso/temp/R-IL7C2/R-IL7C2/10/pdata/1
## Reading sample  R-IL7C3  in  /home/scardoso/temp/R-IL7C3/R-IL7C3/10/pdata/1
## Reading sample  R-IL7C4  in  /home/scardoso/temp/R-IL7C4/R-IL7C4/10/pdata/1
## Reading sample  R-IL7C8  in  /home/scardoso/temp/R-IL7C8/R-IL7C8/10/pdata/1
## Reading sample  R-IL7T1  in  /home/scardoso/temp/R-IL7T1/R-IL7T1/10/pdata/1
## Reading sample  R-IL7T2  in  /home/scardoso/temp/R-IL7T2/R-IL7T2/10/pdata/1
## Reading sample  R-IL7T3  in  /home/scardoso/temp/R-IL7T3/R-IL7T3/10/pdata/1
## Reading sample  R-IL7T4  in  /home/scardoso/temp/R-IL7T4/R-IL7T4/10/pdata/1
## Reading sample  R-IL7T8  in  /home/scardoso/temp/R-IL7T8/R-IL7T8/10/pdata/1
## Reading sample  R-KDC1  in  /home/scardoso/temp/R-KDC1/R-KDC1/10/pdata/1
## Reading sample  R-KDC2  in  /home/scardoso/temp/R-KDC2/R-KDC2/10/pdata/1
## Reading sample  R-KDC3  in  /home/scardoso/temp/R-KDC3/R-KDC3/10/pdata/1
## Reading sample  R-KDC4  in  /home/scardoso/temp/R-KDC4/R-KDC4/10/pdata/1
## Reading sample  R-KDC8  in  /home/scardoso/temp/R-KDC8/R-KDC8/10/pdata/1
## Reading sample  R-KDT1  in  /home/scardoso/temp/R-KDT1/R-KDT1/10/pdata/1
## Reading sample  R-KDT2  in  /home/scardoso/temp/R-KDT2/R-KDT2/10/pdata/1
## Reading sample  R-KDT3  in  /home/scardoso/temp/R-KDT3/R-KDT3/10/pdata/1
## Reading sample  R-KDT4  in  /home/scardoso/temp/R-KDT4/R-KDT4/10/pdata/1
## Reading sample  R-KDT8  in  /home/scardoso/temp/R-KDT8/R-KDT8/10/pdata/1
## Creating dataset (this may take a while)
## Done.

3. Table of the data just loaded:

DT::datatable(nmr_bruker_spectra_dataset$data, options=list(scrollX = TRUE))
## Warning in instance$preRenderHook(instance): It seems your data is too big for client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html

4. Table of the metadata just loaded:

DT::datatable(nmr_bruker_spectra_dataset$metadata)

5. Detection and alignment of peaks:

Here, we want the detected peaks to have a minimum intensity of 5000000. They will also be aligned according to the specmine method, with a step value of 0.03. But first, we have to treat the missing values (details below), otherwize the function to detect and align peaks will not work.

#Treat missing values:
nmr_bruker_spectra_dataset_mv=missingvalues_imputation(nmr_bruker_spectra_dataset, method = "value", value=0)

#Detect and align peaks, now that missing values were treated:
nmr_bruker_peaks_dataset=detect_nmr_peaks_from_dataset(nmr_bruker_spectra_dataset_mv, baseline_tresh = 5000000)
## Spectrum  L-IL308C1-1  has  42  peaks.
## Spectrum  L-IL308C1-2  has  10  peaks.
## Spectrum  L-IL308C1-3  has  8  peaks.
## Spectrum  L-IL308C2-1  has  13  peaks.
## Spectrum  L-IL308C2-2  has  21  peaks.
## Spectrum  L-IL308C2-3  has  7  peaks.
## Spectrum  L-IL308C3-1  has  42  peaks.
## Spectrum  L-IL308C3-2  has  5  peaks.
## Spectrum  L-IL308C3-3  has  6  peaks.
## Spectrum  L-IL308C4-1  has  44  peaks.
## Spectrum  L-IL308C4-2  has  16  peaks.
## Spectrum  L-IL308C4-3  has  15  peaks.
## Spectrum  L-IL308C8-1  has  23  peaks.
## Spectrum  L-IL308C8-2  has  7  peaks.
## Spectrum  L-IL308C8-3  has  13  peaks.
## Spectrum  L-IL308T1-1  has  39  peaks.
## Spectrum  L-IL308T1-2  has  23  peaks.
## Spectrum  L-IL308T1-3  has  9  peaks.
## Spectrum  L-IL308T2-1  has  45  peaks.
## Spectrum  L-IL308T2-2  has  14  peaks.
## Spectrum  L-IL308T2-3  has  4  peaks.
## Spectrum  L-IL308T3-1  has  33  peaks.
## Spectrum  L-IL308T3-2  has  11  peaks.
## Spectrum  L-IL308T3-3  has  6  peaks.
## Spectrum  L-IL308T4-1  has  26  peaks.
## Spectrum  L-IL308T4-2  has  6  peaks.
## Spectrum  L-IL308T4-3  has  5  peaks.
## Spectrum  L-IL308T8-1  has  40  peaks.
## Spectrum  L-IL308T8-2  has  17  peaks.
## Spectrum  L-IL308T8-3  has  17  peaks.
## Spectrum  L-IL7C1-1  has  29  peaks.
## Spectrum  L-IL7C1-2  has  10  peaks.
## Spectrum  L-IL7C1-3  has  6  peaks.
## Spectrum  L-IL7C2-1  has  34  peaks.
## Spectrum  L-IL7C2-2  has  11  peaks.
## Spectrum  L-IL7C2-3  has  8  peaks.
## Spectrum  L-IL7C3-1  has  36  peaks.
## Spectrum  L-IL7C3-2  has  8  peaks.
## Spectrum  L-IL7C3-3  has  8  peaks.
## Spectrum  L-IL7C4-1  has  59  peaks.
## Spectrum  L-IL7C4-2  has  9  peaks.
## Spectrum  L-IL7C4-3  has  6  peaks.
## Spectrum  L-IL7C8-1  has  24  peaks.
## Spectrum  L-IL7C8-2  has  11  peaks.
## Spectrum  L-IL7C8-3  has  7  peaks.
## Spectrum  L-IL7T1-1  has  47  peaks.
## Spectrum  L-IL7T1-2  has  6  peaks.
## Spectrum  L-IL7T1-3  has  11  peaks.
## Spectrum  L-IL7T2-1  has  26  peaks.
## Spectrum  L-IL7T2-2  has  6  peaks.
## Spectrum  L-IL7T2-3  has  7  peaks.
## Spectrum  L-IL7T3-1  has  23  peaks.
## Spectrum  L-IL7T3-2  has  10  peaks.
## Spectrum  L-IL7T3-3  has  11  peaks.
## Spectrum  L-IL7T4-1  has  29  peaks.
## Spectrum  L-IL7T4-2  has  9  peaks.
## Spectrum  L-IL7T4-3  has  7  peaks.
## Spectrum  L-IL7T8-1  has  16  peaks.
## Spectrum  L-IL7T8-2  has  8  peaks.
## Spectrum  L-IL7T8-3  has  12  peaks.
## Spectrum  L-KDC1-1  has  40  peaks.
## Spectrum  L-KDC1-2  has  5  peaks.
## Spectrum  L-KDC1-3  has  5  peaks.
## Spectrum  L-KDC2-1  has  42  peaks.
## Spectrum  L-KDC2-2  has  4  peaks.
## Spectrum  L-KDC2-3  has  6  peaks.
## Spectrum  L-KDC3-1  has  44  peaks.
## Spectrum  L-KDC3-2  has  14  peaks.
## Spectrum  L-KDC3-3  has  8  peaks.
## Spectrum  L-KDC4-1  has  30  peaks.
## Spectrum  L-KDC4-2  has  14  peaks.
## Spectrum  L-KDC4-3  has  4  peaks.
## Spectrum  L-KDC8-1  has  29  peaks.
## Spectrum  L-KDC8-2  has  11  peaks.
## Spectrum  L-KDC8-3  has  12  peaks.
## Spectrum  L-KDT1-1  has  44  peaks.
## Spectrum  L-KDT1-2  has  10  peaks.
## Spectrum  L-KDT1-3  has  6  peaks.
## Spectrum  L-KDT2-1  has  35  peaks.
## Spectrum  L-KDT2-2  has  4  peaks.
## Spectrum  L-KDT2-3  has  5  peaks.
## Spectrum  L-KDT3-1  has  14  peaks.
## Spectrum  L-KDT3-2  has  13  peaks.
## Spectrum  L-KDT3-3  has  6  peaks.
## Spectrum  L-KDT4-1  has  33  peaks.
## Spectrum  L-KDT4-2  has  7  peaks.
## Spectrum  L-KDT4-3  has  5  peaks.
## Spectrum  L-KDT8-1  has  13  peaks.
## Spectrum  L-KDT8-2  has  3  peaks.
## Spectrum  L-KDT8-3  has  4  peaks.
## Spectrum  R-IL308C1  has  13  peaks.
## Spectrum  R-IL308C2  has  25  peaks.
## Spectrum  R-IL308C3  has  22  peaks.
## Spectrum  R-IL308C4  has  21  peaks.
## Spectrum  R-IL308C8  has  22  peaks.
## Spectrum  R-IL308T1  has  28  peaks.
## Spectrum  R-IL308T2  has  19  peaks.
## Spectrum  R-IL308T3  has  19  peaks.
## Spectrum  R-IL308T4  has  27  peaks.
## Spectrum  R-IL308T8  has  15  peaks.
## Spectrum  R-IL7C1  has  7  peaks.
## Spectrum  R-IL7C2  has  26  peaks.
## Spectrum  R-IL7C3  has  19  peaks.
## Spectrum  R-IL7C4  has  38  peaks.
## Spectrum  R-IL7C8  has  22  peaks.
## Spectrum  R-IL7T1  has  23  peaks.
## Spectrum  R-IL7T2  has  15  peaks.
## Spectrum  R-IL7T3  has  21  peaks.
## Spectrum  R-IL7T4  has  10  peaks.
## Spectrum  R-IL7T8  has  12  peaks.
## Spectrum  R-KDC1  has  26  peaks.
## Spectrum  R-KDC2  has  11  peaks.
## Spectrum  R-KDC3  has  24  peaks.
## Spectrum  R-KDC4  has  14  peaks.
## Spectrum  R-KDC8  has  17  peaks.
## Spectrum  R-KDT1  has  25  peaks.
## Spectrum  R-KDT2  has  21  peaks.
## Spectrum  R-KDT3  has  17  peaks.
## Spectrum  R-KDT4  has  9  peaks.
## Spectrum  R-KDT8  has  4  peaks.

6. Table of the data with the peaks detected:

DT::datatable(nmr_bruker_peaks_dataset$data, options=list(scrollX = TRUE))

4.3.2.2 VARIAN data

The example data here used was obtained from the Metabolights database, under the ID MTBLS346. The data was obtained using the get_metabolights_study function.

The folder named “14” was not used in this example (i.e. not present in the data folder here provided), as data was too big to be read. Therefore, its metadata information was also deleted from the metadata file.

1. Strings indicating where data and metadata is:

varian_nmr_spectra_folder="/home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346"
varian_nmr_metadata_file="/home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/metadata_no_14.csv"

2. In this data, the spectra folders’ names do not correspond to the names of the samples, so a file (here represented by variable samples_folders) will be given to the argument samples.names.

samples_folders="/home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/samples_files.csv"

Content of samples_folders file, where the first column corresponds to the names of the samples and the second one to the folders names where the corresponding data is stored:

2. Loading NMR vartian spectral data to specmine:

The folders here read are not zipped (.zip), as they previously uncompressed for purpose of showing how to proceed in such a case. No zero filling will be performed, but apodization will be

nmr_varian_spectra_dataset=read_varian_spectra_raw(varian_nmr_spectra_folder, metadata_file=varian_nmr_metadata_file, samples.names=samples_folders, zipped=F,
                                                   zero_filling=F)
## Reading files:
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/10.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/11.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/12.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/13.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/15.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/16.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/17.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/18.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/19.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/1.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/20.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/21.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/22.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/23.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/24.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/25.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/26.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/27.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/28.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/29.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/2.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/30.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/31.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/32.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/33.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/34.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/35.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/36.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/37.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/38.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/39.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/3.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/40.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/41.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/42.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/43.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/44.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/45.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/4.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/5.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/6.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/7.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/8.fid
## /home/scardoso/Documents/metabolomics_datasets/NMR/Spectra/MTBLS346/9.fid

3. Table of the first 500 data points of the data just loaded:

DT::datatable(nmr_varian_spectra_dataset$data[1:500,], options=list(scrollX = TRUE))

4. Table of the metadata just loaded:

DT::datatable(nmr_varian_spectra_dataset$metadata)

5. Detection and alignment of peaks:

Here, we want the detected peaks to have a minimum intensity of 20000000. They will also be aligned according to the specmine method, with a step value of 0.03.

#Detect and align peaks:
nmr_varian_peaks_dataset=detect_nmr_peaks_from_dataset(nmr_varian_spectra_dataset, baseline_tresh = 20000000)
## Spectrum  THS-GM1_1  has  33  peaks.
## Spectrum  THS-GM1_2  has  33  peaks.
## Spectrum  THS-GM1_3  has  34  peaks.
## Spectrum  THS-GM1_4  has  34  peaks.
## Spectrum  THS-GM1_6  has  32  peaks.
## Spectrum  THS-GM1_7  has  32  peaks.
## Spectrum  THS-GM1_8  has  33  peaks.
## Spectrum  THS-GM1_9  has  32  peaks.
## Spectrum  SIL-WT1  has  35  peaks.
## Spectrum  THS-WT1  has  34  peaks.
## Spectrum  SIL-WT2  has  32  peaks.
## Spectrum  SIL-WT3  has  32  peaks.
## Spectrum  SIL-WT4  has  33  peaks.
## Spectrum  SIL-WT5  has  33  peaks.
## Spectrum  SIL-WT6  has  32  peaks.
## Spectrum  SIL-WT7  has  32  peaks.
## Spectrum  SIL-WT8  has  32  peaks.
## Spectrum  SIL-WT9  has  31  peaks.
## Spectrum  SIL-GM1_1  has  42  peaks.
## Spectrum  SIL-GM1_2  has  32  peaks.
## Spectrum  THS-WT2  has  35  peaks.
## Spectrum  SIL-GM1_3  has  33  peaks.
## Spectrum  SIL-GM1_4  has  65  peaks.
## Spectrum  SIL-GM1_5  has  32  peaks.
## Spectrum  SIL-GM1_6  has  31  peaks.
## Spectrum  SIL-GM1_7  has  30  peaks.
## Spectrum  SIL-GM1_8  has  33  peaks.
## Spectrum  SIL-GM1_9  has  30  peaks.
## Spectrum  SIL-GM2_1  has  31  peaks.
## Spectrum  SIL-GM2_2  has  43  peaks.
## Spectrum  SIL-GM2_3  has  32  peaks.
## Spectrum  THS-WT3  has  36  peaks.
## Spectrum  SIL-GM2_4  has  34  peaks.
## Spectrum  SIL-GM2_5  has  36  peaks.
## Spectrum  SIL-GM2_6  has  33  peaks.
## Spectrum  SIL-GM2_7  has  32  peaks.
## Spectrum  SIL-GM2_8  has  52  peaks.
## Spectrum  SIL-GM2_9  has  31  peaks.
## Spectrum  THS-WT4  has  34  peaks.
## Spectrum  THS-WT5  has  32  peaks.
## Spectrum  THS-WT6  has  33  peaks.
## Spectrum  THS-WT7  has  32  peaks.
## Spectrum  THS-WT8  has  32  peaks.
## Spectrum  THS-WT9  has  34  peaks.

6. Table of the data with the peaks detected:

DT::datatable(nmr_varian_peaks_dataset$data, options=list(scrollX = TRUE))

4.4 GC/LC-MS Spectra (peak detection comes with data reading)

4.4.1 Functions to use

Read MS spectra data and metadata into specmine dataset

read_ms_spectra(folder.name, type = “undefined”,filename.meta = NULL, description = “”, prof.method = “bin”, fwhm = 30, bw = 30, intvalue = “into”, header.col.meta = TRUE, header.row.meta = TRUE, sep.meta = “,”)

  • folder.name: string containing the path of the data folder;

  • type: type of the data. Defaults to “undefined”, but in this case should either be “lcms-spectra” or “gcms-spectra”;

  • filename.meta: string containing the path of the metadata file; optional but recomended

  • description: a short text describing the dataset. optional

  • prof.method: profile generation method. Either “bin”, “binlin”, “binlinbase” or “intlin”. Defaults to “bin”. profmethod parameter from xcmsSet function from xcms package.

  • fwhm: full width at half maximum of matched filtration gaussian model peak. Commonly 30 for LC-MS spectra and 4 for GC-MS spectra. Defaults to 30. fwhm parameter from xcmsSet function from xcms package.

  • bw: bandwidth (standard deviation or half width at half maximum) of the Gaussian smoothing kernel, to apply to the peak density chromatogram. Commonly 30 for LC-MS spectra and 5 for GC-MS spectra. Defaults to 30. bw parameter from group function from xcms package.

  • intvalue: peak intensity measure (intvalue value parameter from groupval function from xcms package). It can either be

    • “into” - integrated area of original (raw) peak. Default value;

    • “intf” - integrated area of filtered peak;

    • “maxo” - maximum intensity of original (raw) peak;

    • “maxf” - maximum intensity of filtered peak;

  • header.col.meta : boolean value (TRUE or FALSE) indicating if the metadata file contains a header column with the name of the metadata variables. Defaults to TRUE.

  • header.row.meta: boolean value (TRUE or FALSE) indicating if the metadata file contains a header row with the name of the samples. Defaults to TRUE.

  • sep.meta: the separator character of the metadata file. Defaults to “,”.

4.4.2 Example

1. Strings indicating where data and metadata is:

lcms_data_folder="/home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data"
lcms_metadata_file="/home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/metadata_lcms.csv"

2. Loading LC-MS data to specmine:

lcms_dataset = read_ms_spectra(lcms_data_folder, type = "lcms-spectra", lcms_metadata_file, description = "lc-ms mice spinal cord samples")
## Processing 3195 mz slices ... OK
## Performing retention time correction using 133 peak groups.
## Processing 3195 mz slices ... OK
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/ko15.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/ko16.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/ko21.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/ko22.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/wt21.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/wt22.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/wt15.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/wt16.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/ko18.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/ko19.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/wt18.CDF 
## method:  bin 
## step:  0.1 
## /home/scardoso/Documents/metabolomics_datasets/MS/Spectra/miceSpinalCord/data/wt19.CDF 
## method:  bin 
## step:  0.1

3. Table of the data just loaded:

DT::datatable(lcms_dataset$data, options=list(scrollX = TRUE))

4. Table of the metadata just loaded:

DT::datatable(lcms_dataset$metadata)

4.5 UV-Vis, IR and Raman Spectra

4.5.1 Functions to use

4.5.1.1 When the data is in a CSV file

Read data and metadata

read_dataset_csv(filename.data, filename.meta = NULL, type = “undefined”, description = “”, label.x = NULL, label.values = NULL, sample.names = NULL, format = “row”, header.col = TRUE, header.row = TRUE, sep = “,”, header.col.meta = TRUE, header.row.meta = TRUE, sep.meta = “,”)

  • filename.data: string containing the path of the data file;

  • filename.meta: string containing the path metadata file; optional but recomended

  • type: type of the data. Defaults to “undefined”. It should be “concentrations”;

  • description: a short text describing the dataset. optional

  • label.x: the label for the x values. optional

  • label.values: the label for the y values. optional

  • sample.names: character vector with the names of the samples, if they are not present in the header row or column;

  • format: format which the data are in the CSV file. It can be

    • “row”: if the samples are in the rows (each line corresponds to a sample);

    • “col” if the samples are in the columns (each column corresponds to a sample).

  • header.col: boolean value (TRUE or FALSE) indicating if the CSV contains a header column with the names of the samples (if format=“row”) or variables (if format=“col”). Defaults to TRUE;

  • header.row: boolean value (TRUE or FALSE) indicating if the CSV contains a header row with the names of the variables (if format=“row”) or samples (if format=“col”). Defaults to TRUE;

  • sep: the separator character. Defaults to “,”;

  • header.col.meta: boolean value indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row.meta: boolean value indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep.meta: the separator character of the metadata file. Defaults to “,”.

4.5.1.2 When the data is in multiple CSV files

1: Read Data into list of data samples

read_csvs_folder(foldername, header=TRUE, sep=“,”, dec=“.”, …)

  • foldername: string containing the path of the data folder;

  • header: boolean value (TRUE or FALSE) indicating whether data files have a header row with the names of the data variables. Defaults to TRUE;

  • sep: the separator character of the data values. Defaults to “,”;

  • dec: character used in the file for decimal points. Defaults to “.”;

  • skip: number of lines of the data file to skip before beginning to read data; optional

  • : additional parameters for read.csv function from utils package.

2: Read metadata file (optional step but recomended)

read_metadata(filename, header.col = T, header.row = T, sep = “,”)

  • filename: string indicating the path of the file with the metadata;

  • header.col: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep: the separator character. Defaults to “,”.

3: Join Data and Metadata (adition of metadata is, as said, optional but recomended) into a specmine dataset

create_dataset(datamatrix, type = “undefined”, metadata = NULL, description = “”, sample.names = NULL, x.axis.values = NULL, label.x = NULL, label.values = NULL)

  • datamatrix: matrix with numerical data: rows are assumed to be variables and columns assumed to be samples. Can be obatined from the function read_csvs_folder above, by transforming the data list obtained into a numeric matrix. See examples for this;

  • type: type of the data. Defaults to “undefined”, but in this case should either be “ir-spectra”, “uvv-spectra” or “raman-spectra”;

  • metadata: data frame containing the metadata. Can be obtained from the function read_metadata above; optional but recomended

  • description: string with a short description of the dataset; optional

  • sample.names: vector with sample names. If NULL then the column names of datamatrix or sequential numbers will be used. Defaults to NULL;

  • x.axis.values: vector with the x axis values. If NULL then the row names of datamatrix or sequential numbers will be used. Defaults to NULL

  • label.x: the label for the x values. optional

  • label.y: the label for the y values. optional

4.5.1.3 When the data is in multiple SPC files

Read Data and Metadata

read_dataset_spc(folder.data, filename.meta = NULL, type = “undefined”, description = “”, nosubhdr = F, label.x = NULL, label.values = NULL, header.col.meta = TRUE, header.row.meta = TRUE, sep.meta = “,”)

  • folder.data: string containing the path of the data folder;

  • filename.meta: string indicating the path of the file with the metadata;

  • type: type of the data. Defaults to “undefined”, but in this case should either be “ir-spectra”, “uvv-spectra” or “raman-spectra”;

  • description: string with a short description of the dataset; optional

  • nosubhdr: boolean value (TRUE or FALSE) indicating if the subheader of the file should be read or not. Defaults to FALSE;

  • label.x: the label for the x values. optional

  • label.values: the label for the y values. optional

  • header.col.meta: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row.meta: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep.meta: the separator character of the metadata file. Defaults to “,”.

4.5.1.4 When the data is in multiple (J)DX files:

Read Data and Metadata

read_dataset_dx(folder.data, filename.meta = NULL, type = “undefined”, description = “”, label.x = NULL, label.values = NULL, header.col.meta = TRUE, header.row.meta = TRUE, sep.meta = “,”)

  • folder.data: string containing the path of the data folder;

  • filename.data: string containing the path of the filename; optional but recomended

  • type: type of data. Defaults to “undefined”, but in this case should either be “ir-spectra”, “uvv-spectra” or “raman-spectra”;

  • description: a short text describing the dataset; optional

  • label.x: the label for the x values; optional

  • label.values: the label for the y values; optional

  • header.col.meta: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row.meta: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep.meta: the separator character of the metadata file. Defaults to “,”.

4.5.1.5 When the data is in multiple XLSX files:

To read XLSX data files into specmine, the function read.xlsx must be used to read each data file. See example for a better understanding of the overall steps to perform.

1: Read Data (function from xlsx paxkage)

Only the more imnportant arguments for the case of metabolomics spectra will be explained bellow. For further information, go here.

read.xlsx(file, sheetIndex,header=TRUE, …)

  • file: string containing the path of one of the data files;

  • sheetIndex: a number representing the sheet index in the workbook. Normally should be 1 (the first sheet in the workbook);

  • header: a boolean value (TRUE or FALSE) indicating whether the first row contains the names of the columns.

2: Read metadata file (optional step but recomended)

read_metadata(filename, header.col = T, header.row = T, sep = “,”)

  • filename: string indicating the path of the file with the metadata;

  • header.col: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row: boolean value (TRUE or FALSE) indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep: the separator character. Defaults to “,”.

3: Join Data and Metadata (adition of metadata is, as said, optional but recomended) into a specmine dataset

create_dataset(datamatrix, type = “undefined”, metadata = NULL, description = “”, sample.names = NULL, x.axis.values = NULL, label.x = NULL, label.values = NULL)

  • datamatrix: matrix with numerical data: rows are assumed to be variables and columns assumed to be samples. Can be obatined from the function read_csvs_folder above, by transforming the data list obtained into a numeric matrix. See examples for this;

  • type: type of the data. Defaults to “undefined”, but in this case should either be “ir-spectra”, “uvv-spectra” or “raman-spectra”;

  • metadata: data frame containing the metadata. Can be obtained from the function read_metadata above; optional but recomended

  • description: string with a short description of the dataset; optional

  • sample.names: vector with sample names. If NULL then the column names of datamatrix or sequential numbers will be used. Defaults to NULL;

  • x.axis.values: vector with the x axis values. If NULL then the row names of datamatrix or sequential numbers will be used. Defaults to NULL

  • label.x: the label for the x values. optional

  • label.y: the label for the y values. optional

4.5.2 Example(s)

4.5.2.1 Data in CSV File (IR Spectra)

1: Strings indicating where data and metadata is:

ir_spectra_data_file="/home/scardoso/Downloads/cassavaPPD/data_cassava.csv"
ir_spectra_metadata_file="/home/scardoso/Downloads/cassavaPPD/metadata_ir.csv"

2: Read data and metadata files:

ir_spectra_dataset=read_dataset_csv(ir_spectra_data_file, ir_spectra_metadata_file, type = "ir-spectra", format = "col")

3. Table of the data just loaded:

DT::datatable(ir_spectra_dataset$data[1:100,], options=list(scrollX = TRUE))

4. Table of the metadata just loaded:

DT::datatable(ir_spectra_dataset$metadata)

4.5.2.2 XLSX Files (UV-Vis Spectra)

1: Strings indicating where data and metadata is:

uvvis_spectra_data_folder="/home/scardoso/Downloads/Cassava Carotenoids/data"
uvvis_spectra_metadata_file="/home/scardoso/Downloads/Cassava Carotenoids/metadata_uvvis.csv"

2: First, we have to know the number of rows in the files, which are the number of data points, equal to all XLSX files:

#List of file paths in the data folder:
files = list.files(uvvis_spectra_data_folder, full.names = T)

#Names of the files in the data folder:
fileNames = list.files(uvvis_spectra_data_folder)

#Read one of the files and get the number of rows and the values of the data points:
file = xlsx::read.xlsx(files[1], sheetIndex = 1, header=F)
nrows = nrow(file) #number of rows
rnames = file[,1] #Values of the data points

3: Prepare data matrix to read to there the spectra data:

#Data matrix:
datamat = matrix(nrow = nrows, ncol = length(files))

#Rownames of the data matrix (values of data points):
rownames(datamat) = rnames

#Colnames of data matrix (samples names). Here, it is considered that the samples names are the names given to the files:
ext = paste('.', tools::file_ext(fileNames[1]), sep = '') #Gets the extension of the data files, so that it is taken away from the file name and only
                                                  #the name of the file is inserted 
colnames(datamat) = gsub(ext, "", fileNames)

4: Read data files into data matrix:

for (i in 1:length(files)){
  tab_excel = xlsx::read.xlsx(files[i], sheetIndex = 1, header = F)
  datamat[,i] = c(tab_excel[,2], rep(NA, nrows-length(tab_excel[,2]))) #If there's missing data
}

5: Read metadata file:

uvvis_spectra_metadata=read_metadata(uvvis_spectra_metadata_file)

6: Join data and metadata into the specmine dataset:

uvvis_spectra_dataset=create_dataset(datamat, uvvis_spectra_metadata, type = "uvv-spectra")

7. Table of the data just loaded:

DT::datatable(uvvis_spectra_dataset$data[1:100,], options=list(scrollX = TRUE))

8. Table of the metadata just loaded:

DT::datatable(uvvis_spectra_dataset$metadata, options=list(scrollX = TRUE))

4.6 Concentrations Data

4.6.1 Functions to use

Read data and metadata

read_dataset_csv(filename.data, filename.meta = NULL, type = “undefined”, description = “”, label.x = NULL, label.values = NULL, sample.names = NULL, format = “row”, header.col = TRUE, header.row = TRUE, sep = “,”, header.col.meta = TRUE, header.row.meta = TRUE, sep.meta = “,”)

  • filename.data: string containing the path of the data file;

  • filename.meta: string containing the path metadata file; optional but recomended

  • type: type of the data. Defaults to “undefined”. It should be “concentrations”;

  • description: a short text describing the dataset. optional

  • label.x: the label for the x values. optional

  • label.values: the label for the y values. optional

  • sample.names: character vector with the names of the samples, if they are not present in the header row or column;

  • format: format which the data are in the CSV file. It can be

    • “row”: if the samples are in the rows (each line corresponds to a sample);

    • “col” if the samples are in the columns (each column corresponds to a sample).

  • header.col: boolean value (TRUE or FALSE) indicating if the CSV contains a header column with the names of the samples (if format=“row”) or variables (if format=“col”). Defaults to TRUE;

  • header.row: boolean value (TRUE or FALSE) indicating if the CSV contains a header row with the names of the variables (if format=“row”) or samples (if format=“col”). Defaults to TRUE;

  • sep: the separator character. Defaults to “,”;

  • header.col.meta: boolean value indicating if the metadata CSV file contains a header column with the name of the metadata variables. Defaults to TRUE;

  • header.row.meta: boolean value indicating if the metadata CSV file contains a header row with the name of the samples. Defaults to TRUE;

  • sep.meta: the separator character of the metadata file. Defaults to “,”.

4.6.2 Example

1. Strings indicating where data and metadata is:

concentrations_data_file="/home/scardoso/Documents/metabolomics_datasets/concentrations/cachexia/human_cachexia.csv"
concentrations_metadata_file="/home/scardoso/Documents/metabolomics_datasets/concentrations/cachexia/metadata_concentrations.csv"

2. Read concentrations data file and metadata file:

concentrations_dataset=read_dataset_csv(concentrations_data_file, filename.meta = concentrations_metadata_file, type = "concentrations", description = "Metabolite concentrations from samples with or without cachexia", label.x = "metabolites", label.values = "concentration", format = "row")

3. Table of the data just loaded:

DT::datatable(concentrations_dataset$data, options=list(scrollX = TRUE))

4. Table of the metadata just loaded:

DT::datatable(concentrations_dataset$metadata)

5 Upload Data from Metabolights Database

5.1 Function to use

TO USE THIS FUNCTION YOU MUST HAVE PYTHON3 INSTALLED, WITH THE MODULES isatools, os, ftplib, glob, logging, pandas, tempfile, shutil and re INSTALLED

get_metabolights_study(mtblsID, directory)

  • mtblsID: string indicating the ID of the metabolights study to download;

  • directory: string indicating the directory to which the data files of the study will be downloaded. The data will be stored in “/directory/mtblsID”.

5.2 Example

get_metabolights_study("MTBLS131", "/home/scardoso/Documents")
## Downloading files... please wait
## 
## Done.

6 Data Visualization

6.1 Dataset Summary

This function provides a summary of the main features of the dataset:

  • Description;

  • Typr of data;

  • Number of samples;

  • Number of datapoints;

  • Number of metadata variables, if there is metadata;

  • Labels of x axis values and data points, if labels were given.

  • If the user wants to know some statistical properties:

    • Number of missing values in data;

    • Mean of data values;

    • Median of data values;

    • Standard deviation;

    • Range of values;

    • Quantiles;

6.1.1 Function to use

sum_dataset(dataset, stats = T)

  • dataset: a specmine dataset;

  • stats: boolean value (TRUE or FALSE) indicating whether some global statistics of the data values should be calculated. Defaults to TRUE

6.1.2 Example

Example makes use of an LC-MS dataset (LC-MS dataset used).

1. Data summary with statistics:

sum_dataset(lcms_dataset)
## Dataset summary:
## Valid dataset
## Description:  lc-ms mice spinal cord samples 
## Type of data:  lcms-spectra 
## Number of samples:  12 
## Number of data points 410 
## Number of metadata variables:  1 
## Label of x-axis values:  mz/rt 
## Label of data points:  intensity 
## Number of missing values in data:  0 
## Mean of data values:  1337915 
## Median of data values:  279775 
## Standard deviation:  3641583 
## Range of values:  0 45173156 
## Quantiles: 
##       0%      25%      50%      75%     100% 
##        0   111153   279775   845985 45173156

2. Data summary without statistics:

sum_dataset(lcms_dataset, stats=F)
## Dataset summary:
## Valid dataset
## Description:  lc-ms mice spinal cord samples 
## Type of data:  lcms-spectra 
## Number of samples:  12 
## Number of data points 410 
## Number of metadata variables:  1 
## Label of x-axis values:  mz/rt 
## Label of data points:  intensity

6.2 Boxplots

6.2.1 Boxplot of one or more variables distribution

6.2.1.1 Function to use

boxplot_variables(dataset, variables = NULL, samples = NULL, horizontal = T, col = “lightblue”, nchar.label = 10, cex.axis = 0.8, …)

  • dataset: a specmine dataset;

  • variables: vector with the names of the variables to plot. If no names are given (variables=NULL), all variables are plotted. Defaults to NULL;

  • samples: vector with the names of the samples to take into consideration when plotting the variables. If no names are given all samples are taken into consideration (samples=NULL). Defaults to NULL;

  • horizontal: boolean value (TRUE or FALSE) indicating if the boxplots should be drawn horizontaly or not. Defaults to TRUE;

  • col: string that represents the color of the bodies of the boxplots. Can either be “lightblue” (default value),

  • nchar.label: number of characters of variables’ names to display. If a variable has more than nchar.label characters, only the first nchar.label characters will be drawn. Defaults to 10;

  • cex.axis: numeric value that indicates the amount by which the axis is magnified relative to the default. Defaults to 0.8;

  • : additional parameters of boxplot function.

6.2.1.2 Example

Example makes use of an concentrations dataset (concentrations dataset used).

1. Boxplot of all variables distribution through all samples, with boxes diposed vertically

boxplot_variables(concentrations_dataset, horizontal=F, nhar.label=50, cex.axis=1)

2. Boxplot of “Sucrose”, “Histidine” and “Acetate” distribution through all samples, with a title

boxplot_variables(concentrations_dataset, variables=c("Sucrose", "Histidine", "Acetate"), horizontal=F, nhar.label=50, cex.axis=1.5, cex.main=1.5, main="Sucrose, Histidine and Acetate distribution on the dataset")

6.2.2 Boxplot of a variable distribution over two metadata variables

This function can only be used for datasets that have more than one metadata variable.

6.2.2.1 Function to use

plotvar_twofactor(dataset, variable, meta.var1, meta.var2, colour = “darkblue”, title = “”, xlabel = NULL, ylabel = NULL)

  • dataset: a specmine dataset;

  • variable: name of the variable to plot;

  • meta.var1: the name of one of the metadata variables to plot;

  • meta.var2: the name of a different metadata variable to plot from the first one;

  • colour: Colour of the boxes. Defaults to “darkblue”;

  • title: title of the plot; optional

  • xlabel: label of the xx axis; optional

  • ylabel: label of the yy axis; optional

6.2.2.2 Example

Example makes use of an nmr-peaks dataset (NMR peaks dataset used).

1. Plot the intensity distribution of the variable 0.16 ppm over the metadata variables “seasons” and “agroregions”

plotvar_twofactor(nmr_peaks_dataset, "0.16", "seasons", "agroregions", colour = "cyan")
## Warning: Removed 46 rows containing non-finite values (stat_boxplot).

In this case, as warned, some samples had to be excluded to produce the plot, as there were missing values.

6.2.3 Boxplot of one or more variables distribution over one metadata variable

6.2.3.1 Function to use

boxplot_vars_factor(dataset, meta.var, variables = NULL, samples = NULL, horizontal = F, nchar.label = 10, col = NULL, vec.par = NULL, cex.axis = 0.8, ylabs = NULL, …)

  • dataset: a specmine dataset;

  • meta.var: name of the metadata variable to plot;

  • variables: names of the variables to plot;

  • samples: vector with the names of the samples to take into consideration when plotting the variables. If no names are given all samples are taken into consideration (samples=NULL). Defaults to NULL;

  • horizontal: boolean value (TRUE or FALSE) indicating if the boxplots should be drawn horizontaly or not. Defaults to TRUE;

  • nchar.label: number of characters of variables’ names to display. If a variable has more than nchar.label characters, only the first nchar.label characters will be drawn. Defaults to 10;

  • col: vector with the different colors for each box (boxes of the same class of metadata variable will have the same color - example 2.). If not provided, default colors will be used, being in this case one different color for all boxes of a data variable (example 1.). Defaults to NULL;

  • vec.par: if more than one variable is chosen, a vector specifying the disposition of the plots could be useful. This vector should have two elements - c(number_rows, number_columns);

  • cex.axis: numeric value that indicates the amount by which the axis is magnified relative to the default. Defaults to 0.8;

  • ylabs: yy axis label; optional

    • : additional parameters of boxplot function.

6.2.3.2 Example

Example makes use of an concentrations dataset (concentrations dataset used).

1. Boxplot of three data variables (“Creatine”, “Serine” and “Lactate”) over one metadata variable (“Muscle.loss”). The plots of the two first variables will be plotted in the first row, side by side, and the other one in the second row. Boxes here are colored according to the data variable they represent.

boxplot_vars_factor(concentrations_dataset, "Muscle.loss", variables = c("Creatine","Serine", "Lactate"), cex.axis=2, cex.main=2, vec.par=c(2,2))

2. Boxplot of three data variables (“Creatine”, “Serine” and “Lactate”) over one metadata variable (“Muscle.loss”). The plots of the two first variables will be plotted in the first row, side by side, and the other one in the second row.Boxes here are colored according to the metadata class they represent.

Here, colors are set automaticaly:

boxplot_vars_factor(concentrations_dataset, "Muscle.loss", variables = c("Creatine","Serine", "Lactate"), cex.axis=1.5, cex.main=2, vec.par=c(2,2), col=1:length(levels(get_metadata(concentrations_dataset)[,"Muscle.loss"])))

And here, two specific colors are chosen (as there are only two classes on the metadata variable chosen). If you don’t know how many classes there are, you can type similarly to the following: length(levels(get_metadata(concentrations_dataset)[,“Muscle.loss”])).

boxplot_vars_factor(concentrations_dataset, "Muscle.loss", variables = c("Creatine","Serine", "Lactate"), cex.axis=1.5, cex.main=2, vec.par=c(2,2), col=c("green", "red"))

6.3 Spectra plot

Only makes sense for data of spectral type.

6.3.1 Function to use

plot_spectra(dataset, column.class, func = NULL, samples = NULL, variable.bounds = NULL, xlab = NULL, ylab = NULL, lty = 1, legend.place = “topright”, cex = 0.8, reverse.x = F, …)

  • dataset: a specmine dataset;

  • meta.var: name of a metadata variable by which each spectrum sample will be colored;

  • fun: function to compute the summary statistics to apply to the data. Defaults to NULL; optional

  • samples: vector with the names of the samples to plot. If no names are given all samples are plotted (samples=NULL). Defaults to NULL;

  • variable.bounds: numeric vector with two elements indicating the interval of x-values to plot; optional

  • xlab: xx axis label; optional

  • ylab: yy axis label; optional

  • legend.place: string indicating the place where the legend’s box will be placed. Can either be: “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”;

  • cex: numeric value indicating the relative size of the legend. Defaults to 0.8;

  • reverse.x: boolean value (TRUE or FALSE) ndicating if the x-axis will be shown reversed (decreasingly) or not. Defaults to FALSE;

  • lty: line type (parameter of matplot function);

  • : additional parameters of matplot function.

6.3.2 Example

Example makes use of an UV-Vis dataset (UV-Vis dataset used).

1: Spectra plot of all samples, coloured by a metadata variable (here is “varieties”):

plot_spectra(uvvis_spectra_dataset, "varieties", cex=1)

2: Spectra plot of all samples, coloured by a metadata variable (here is “colors”), between the xx values 400 and 600, giving labels to the axis and a title:

plot_spectra(uvvis_spectra_dataset, "colors", variable.bounds=c(400,600), xlab="wavelength", ylab="Absorbance", main="Spectra plot", cex=1.2)

3: Spectra plot of four samples (one from each color), coloured by the metadata variable “colors”, with the xx axis values decreasing:

plot_spectra(uvvis_spectra_dataset, "colors", samples=c("3.1", "5.1", "7.1", "11.1"), reverse.x=T, cex=1.2)

6.4 Peaks Plot

6.4.1 Function to use

plot_peaks(dataset, column.class, func = NULL, samples = NULL, variable.bounds = NULL, xlab = NULL, ylab = NULL, legend.place = “topright”, cex = 0.8, reverse.x = F, p.size=0.5, …)

  • dataset: a specmine dataset;

  • meta.var: name of a metadata variable by which each spectrum sample will be colored;

  • samples: vector with the names of the samples to plot. If no names are given all samples are plotted (samples=NULL). Defaults to NULL;

  • variable.bounds: numeric vector with two elements indicating the interval of x-values to plot; optional

  • xlab: xx axis label; optional

  • ylab: yy axis label; optional

  • legend.place: string indicating the place where the legend’s box will be placed. Can either be: “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”;

  • cex: numeric value indicating the relative size of the legend. Defaults to 0.8;

  • reverse.x: boolean value (TRUE or FALSE) ndicating if the x-axis will be shown reversed (decreasingly) or not. Defaults to FALSE;

  • p.size: numeric value indicating the relative size of the plot points. Defaults to 0.5;

  • : additional parameters of matplot function.

6.5 Samples’ Statistics

6.5.1 Function to use

stats_by_sample(dataset, samples = NULL)

  • dataset: s specmine dataset;

  • samples: vector with the names of the samples to take into consideration when calculating the statistics. If no names are given all samples are used (samples=NULL). Defaults to NULL.

6.5.2 Example

Example makes use of an concentrations dataset (concentrations dataset used).

1: Statistics on all samples:

samps.stats=stats_by_sample(concentrations_dataset)
DT::datatable(samps.stats, options=list(scrollX=TRUE))

6.6 Variable’s Statistics

6.6.1 Function to use

stats_by_variable(dataset, variables = NULL, variable.bounds = NULL)

  • dataset: a specmine dataset;

  • variables: vector with the variables to use to calculate the statistics. If numeric vector, it is assumed that they represent indexes. If no variables are given, all are used (variables=NULL). Defaults to NULL;

  • variable.bounds: if the variables are numeric, you can choose to select the interval of variables to take into consideration, so that you do not need to select every single data point wanted. Again, if no variables are given, all are used (variables=NULL). Defaults to NULL.

6.6.2 Example

Example makes use of an concentrations dataset (concentrations dataset used).

1: Statistics on all variables:

vars.stats=stats_by_variable(concentrations_dataset)
DT::datatable(vars.stats, options=list(scrollX=TRUE))

7 Pre-Processing

7.1 Missing Values

The treatment of missing values can be done by replacing them with another value.

missingvalues_imputation(dataset, method = “value”, value = 5e-04, k = 5)

  • dataset: a specmine dataset;

  • method: method by which to treat the missing values. It can either be

    • “value”: replaces the missing values with a specific value (given in value argument);

    • “mean”: replaces the missing values with the mean of the variables’ values;

    • “median”: replaces the missing values with the median of the variables’ values

    • “knn”: replaces the missing values with k nearest neighbor (given in k argument) averaging;

    • “linapprox”: replaces the missing values with linear approximation.

  • value: the value to replace the missing values if the method “value” is chosen;

  • k: the number of neighbors if the method “knn” is chosen.

7.2 Remove Data

7.2.1 Remove data variables

7.2.1.1 Function to use

You can remove specific data variables from the dataset

remove_data_variables(dataset, variables.to.remove, by.index = FALSE)

  • dataset: a specmine dataset;

  • variables.to.remove: vector with the indexes or names of the variables to remove;

  • by.index: boolean value (TRUE or FALSE) indicating if the values in variables.to.remove argument are indexes or not. Defaults to FALSE;

You can remove an interval of data variables. Should not be used for concentrations data

remove_x_values_by_interval(dataset, min.value, max.value)

  • dataset: a specmine dataset;

  • min.value: the minimum value of the interval;

  • max.value: the maximum value of the interval.

7.2.1.2 Example

Example makes use of a concentrations dataset (concentrations dataset used).

1. Remove the variables Creatine and Serine from the dataset:

remove_vars_concentrations_dataset = remove_data_variables(concentrations_dataset, c("Creatine","Serine"))

2. Data table after removing the variables from the dataset:

DT::datatable(remove_vars_concentrations_dataset$data, options=list(scrollX = TRUE))

7.2.2 Remove metadata variables

7.2.2.1 Function to use

remove_metadata_variables(dataset, variables.to.remove)

  • dataset: a specmine dataset;

  • variables.to.remove: vector with the names of the metadata variables to remove.

7.2.2.2 Example

Example makes use of an IR dataset (IR dataset used).

1. Remove the metadata variable varieties from the dataset:

remove_meta_ir_spectra_dataset=remove_metadata_variables(ir_spectra_dataset, "varieties")

2. Metadata table after removing the metadata variables from the dataset:

DT::datatable(remove_meta_ir_spectra_dataset$metadata, options=list(scrollX = TRUE))

7.2.3 Remove samples

7.2.3.1 Function to use

remove_samples(dataset, samples.to.remove, rebuild.factors = T)

  • dataset: a specmine dataset;

  • samples.to.remove: vector with the names of the samples to remove from the dataset;

  • rebuild.factors: boolean value (TRUE or FALSE) indicating if the metadata factors should be rebuilded after the subset. Defaults to TRUE.

7.2.3.2 Example

Example makes use of a concentrations dataset (concentrations dataset used).

1. Remove the variables PIF_178 and PIF_090 from the dataset:

remove_samps_concentrations_dataset = remove_samples(concentrations_dataset, c("PIF_178","PIF_090"))

2. Data table after removing the samples from the dataset:

DT::datatable(remove_samps_concentrations_dataset$data, options=list(scrollX = TRUE))
DT::datatable(remove_samps_concentrations_dataset$metadata, options=list(scrollX = TRUE))

7.3 Remove Data according to presence of missing values

7.3.1 Remove samples

7.3.1.1 Functions to use

You can remove samples according to the amount of missing values present in each sample

remove_samples_by_nas(dataset, max.nas = 0, by.percent = F)

  • dataset: a specmine dataset;

  • max.nas: maximum number or percentage of missing values that a sample can have. Samples with more missing values than the ones allowed are removed. Defaults to 0;

  • by.percent: boolean value (TRUE or FALSE) indicating if the value of the max.nas argument is a percentage or not. Defaults to FALSE.

You can remove samples if the corresponding value of a metadata variable is missing

remove_samples_by_na_metadata(dataset, metadata.var)

  • dataset: a specmine dataset;

  • metadata.var: name of the metadata variable by which the samples could be removed if the value is missing.

7.3.2 Remove variables

7.3.2.1 Function to use

You can remove variables according to the amount of missing values present in each variable

remove_variables_by_nas(dataset, max.nas = 0, by.percent = F)

  • dataset: a specmine dataset;

  • max.nas: maximum number or percentage of missing values that a variable can have. Variables with more missing values than the ones allowed are removed. Defaults to 0;

  • by.percent: boolean value (TRUE or FALSE) indicating if the value of the max.nas argument is a percentage or not. Defaults to FALSE.

7.4 Data Transformation

The data can be transformed by one of two methods: logarithmic transformation or cubic root transformation.

transform_data(dataset, method = “log”)

  • dataset: a specmine dataset;

  • method: string specifying the transformation method. It can either be

    • “log”: logarithmic transformation;

    • “cubicroot”: cubic root transformation.

7.5 Scaling

The data can be scaled according to one of three methods: auto, range or parto scaling.

scaling(dataset, method = “auto”)

  • dataset: a specmine dataset;

  • method: string specifying the scaling method. It can either be “auto”, “range” or “pareto”.

7.6 Correction

Three methods are available to perform data correction: background, offset and baseline corrections. This should only be applied to spectral data.

data_correction(dataset, type = “background”, method = “modpolyfit”, …)

7.7 Smoothing Interpolation

Smoothing interpolation with methods Bin, Loess and Savitzky-Golay.

smoothing_interpolation(dataset, method = “bin”, reducing.factor = 2, x.axis = NULL, p.order = 3, window = 11, deriv = 0)

  • dataset: a specmine dataset;

  • method: string specifying the smoothing method. It can either be “bin”, “loess”, “savitzky.golay”. It defaults to “bin”;

  • reducing.factor: if method “bin” is chosen, the numeric value indicating the reducing factor should be given. It defaults to 2;

  • x.axis: if method “loess” is chosen, a numeric vector representing the new x-axis for loess method can be given. Defaults to NULL;

  • p.order: if method “savitzky.golay” is chosen, a numeric value representing the polynomial order should be given. It defaults to 3;

  • window: if method “savitzky.golay” is chosen, a odd numeric value indicating the size of the window. It defaults to 11;

  • deriv: if method “savitzky.golay” is chosen, a numeric value indicating the differentiation order. It defaults to 0.

7.8 Convert to Factor

Metadata variables can be converted to factors (useful in some analysis, for example, train classifcation models and color plots based on a metadata variable):

convert_to_factor(dataset, metadata.var)

  • dataset: a specmine dataset;

  • metadata.var: name of the metadata variable to convert.

7.9 Mean Centering

mean_centering(dataset)

  • dataset: a sepcmine dataset.

7.10 First Derivative

first_derivative(dataset)

  • dataset: a specmine dataset.

7.11 Multiplicative Scatter Correction

msc_correction(dataset)

  • dataset: a specmine dataset.

7.12 Data Normalization

Data normalization can be performed through the sum, median, a reference sample or a reference variable

normalize(dataset, method, ref = NULL, constant = 1000)

  • dataset: a specmine dataset;

  • _ method_: string specifying the normalization method. It can either be

    • “sum”: normalization by the sum of a constant, given in the constant argument;

    • “median”: normalization by the median;

    • “ref.sample”: normalization by a reference sample, given in the ref argument;

    • “ref.feature”: normalization by a reference variable, given in the ref argument.

  • ref: if method “ref.sample” or ref.feature“” is chosen, a string indicating the sample or variable of reference must be given;

  • constant: if method “sum” is chosen, the constant by which to do the sum normalization must be given.

7.13 Subset dataset by variables

7.13.1 Functions to use

You can subset the dataset by only keeping data from specific variables

subset_x_values(dataset, variables, by.index = FALSE)

  • dataset: a specmine dataset;

  • variables: vector with the indexes or names of the variables to keep;

  • by.index: boolean value (TRUE or FALSE) indicating if the values in variables argument are indexes or not. Defaults to FALSE;

You can subset the dataset by keeping data from an interval of variables. Only for data whose variables are numeric, i.e., should not be used for concentrations data

subset_x_values_by_interval(dataset, min.value, max.value)

  • dataset: a specmine dataset;

  • min.value: the minimum value of the interval;

  • max.value: the maximum value of the interval.

7.13.2 Examples

Example makes use of an UV-Vis dataset (UV-Vis dataset used).

1. Subset dataset to only keep the wavelength values between 400 and 600:

subset_vals_int_uvvis_spectra_dataset=subset_x_values_by_interval(uvvis_spectra_dataset, 400, 600)

2. Data and metadata tables after subsetting the dataset:

DT::datatable(subset_vals_int_uvvis_spectra_dataset$data, options=list(scrollX = TRUE))
DT::datatable(subset_vals_int_uvvis_spectra_dataset$metadata, options=list(scrollX = TRUE))

7.14 Subset dataset by samples

7.14.1 Functions to use

You can subset the dataset by only keeping data from specific samples

subset_samples(dataset, samples, rebuild.factors = T)

  • dataset: a specmine datatset;

  • samples: vector with the indexes or names of the samples to keep;

  • rebuild.factors: boolean value (TRUE or FALSE) indicating if the metadata factors should be rebuilded after the subset. Defaults to TRUE.

You can subset the dataset by keeping samples that have certain metadata classe(s)

subset_samples_by_metadata_values(dataset, metadata.varname, values)

  • dataset: a specmine dataset;

  • metadata.varname: string representing the metadata variable by whose values you will want to keep the samples;

  • values: the samples that are kept must have the metadata classe(s) of the metadata variable chosen (metadata.varname) specified in this argument, using a character vector with the classe(s).

7.14.2 Examples

Example makes use of an nmr-peaks dataset (NMR peaks dataset used).

1. Subset dataset to only keep the samples AC_au, AC_sm, AC_sp, AC_wi:

subset_samp_nmr_peaks_dataset=subset_samples(nmr_peaks_dataset, c("AC_au", "AC_sm", "AC_sp", "AC_wi"))

2. Data and metadata tables after subsetting the dataset:

DT::datatable(subset_samp_nmr_peaks_dataset$data, options=list(scrollX = TRUE))
DT::datatable(subset_samp_nmr_peaks_dataset$metadata, options=list(scrollX = TRUE))

3. Subset dataset to only keep samples from winter (wi) and summer (sm) seasons:

subset_samp_met_nmr_peaks_dataset=subset_samples_by_metadata_values(nmr_peaks_dataset, "seasons", c("wi", "sm"))

4. Data and metadata tables after subsetting the dataset:

DT::datatable(subset_samp_met_nmr_peaks_dataset$data, options=list(scrollX = TRUE))
DT::datatable(subset_samp_met_nmr_peaks_dataset$metadata, options=list(scrollX = TRUE))

7.15 Subset dataset by samples and variables

7.15.1 Functions to use

You can subset the datast by keeping specific samples and variables

subset_by_samples_and_xvalues(dataset, samples, variables = NULL, by.index = F, variable.bounds = NULL, rebuild.factors = T)

  • dataset: a specmine dataset;

  • samples: vector with the indexes or names of the samples to keep;

  • variables: vector with the indexes or names of the variables to keep. Defaults to NULL;

  • by.index: boolean value (TRUE or FALSE) indicating if the values in variables and samples arguments are indexes or not. Defaults to FALSE;

  • variable.bounds: numeric vector of two elements with the minimum and maximum values of the interval of variables to keep. Argument should not be used for concentrations dataset. When used, arguments by.index should be FALSE and variables NULL. Defaults to NULL;

  • rebuild.factors: boolean value (TRUE or FALSE) indicating if metadata factors should be rebuilded. Defaults to TRUE.

7.15.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Subset the dataset to only keep the data from samples PIF_178 and NETL_022_V1, and from variables Creatinine and Serine.

subset_vars_samp_concentrations_dataset = subset_by_samples_and_xvalues(concentrations_dataset, c("PIF_178","NETL_022_V1"), variables = c("Creatine","Serine"))

2. Data and metadata tables after subsetting the dataset:

DT::datatable(subset_vars_samp_concentrations_dataset$data, options=list(scrollX = TRUE))
DT::datatable(subset_vars_samp_concentrations_dataset$metadata, options=list(scrollX = TRUE))

7.16 Low-level Data Fusion

7.16.1 Function to use

Datasets from different types of data can be joined together. Only the samples that are the same in both datasets will appear in the final one.

low_level_fusion(datasets)

  • datasets: List of specmine datasets to join in one.

7.17 Aggregate Samples

7.17.1 Function to use

aggregate_samples(dataset, indexes, aggreg.fn = “mean”, meta.to.remove = c())

  • dataset: a specmine dataset;

  • indexes: numeric vector indicating how samples will be grouped. For example, the vector c(1,2,2,3,1,3) indicates that the first and fifth samples are grouped in one, the second and third in another one, and the forth and last one are aggregated in another one;

  • aggreg.fn: string representing the name of the function by which the samples will be aggregated. It can either be “mean”, “median”, “sum”, “max” (maximum value) or “min” (minimum value). It defaults to “mean”;

  • meta.to.remove: vector with the name(s) of the variable(s) you want to remove after the aggrgation. optional

7.17.2 Examples

Example makes use of an nmr-peaks dataset (NMR peaks dataset used).

1. In the following example, the samples are aggregated according to the classes in the metadata variable “seasons”, i.e., samples with the same class on the metadata variable will be grouped together.

#Number of different metadata variable's values will be the number of different groups in which the samples will be grouped:
x=1:length(levels(nmr_peaks_dataset$metadata[["seasons"]]))
names(x)=levels(nmr_peaks_dataset$metadata[["seasons"]])
indexes=c()
for (meta in nmr_peaks_dataset$metadata[["seasons"]]){
  indexes=c(indexes, x[meta])
}

#The "agroregions" metadata variable is removed after samples are grouped, as it stops making sense for it to be there
aggregated_nmr_peaks_dataset = aggregate_samples(nmr_peaks_dataset, indexes, meta.to.remove="agroregions")

2. Data and Metadata tables after aggregation:

DT::datatable(aggregated_nmr_peaks_dataset$data, options=list(scrollX=TRUE))
DT::datatable(aggregated_nmr_peaks_dataset$metadata, options=list(scrollX=TRUE))

7.18 Flat Pattern Filter

7.18.1 Function to use

flat_pattern_filter(dataset, filter.function = “iqr”, by.percent = T, by.threshold = F, red.value = 0)

  • dataset: a specmine dataset;

  • filter.function: function by which to filter the data variables. It can either be

    • “iqr”: Interquantile Range;

    • “rsd”: Relative Standard Deviation;

    • “mad”: Median Absolute Deviation;

    • “mean”: Mean;

    • “median”: Median.

  • by.percent: boolean value (TRUE or FALSE) indicating if the amount of variables to filter in the dataset is given in a percentage. It defaults to TRUE;

  • by.threshold: boolean value (TRUE or FALSE) indicating if the amount of variables to filter in the dataset is defined by wether they are under or not a given threshold. It defaults to FALSE;

  • red.value: numeric value indicating a percentage or threshold. It can also be “auto”, indicating that the number of variables to remove are calculated automatically.

7.19 Replace specific data or metadata values in the dataset

7.19.1 Functions to use

You can replace a data value for a new one on the dataset

replace_data_value(dataset, x.axis.val, sample, new.value, by.index = F)

  • dataset: a specmine dataset;

  • x.axis.val: index or name of the variable that corresponds to the data point to replace;

  • sample: name of the sample that corresponds to the data point to replace;

  • new.value: new value (numeric) of the data point;

  • by.index: boolean value (TRUE or FALSE) indicating if the value in x.axis.val argument is an index or not. Defaults to FALSE;

You can replace a metadata value for a new one on the dataset

replace_metadata_value(dataset, variable, sample, new.value)

  • dataset: a specmine dataset;

  • variable: name of the metadata variable that corresponds to the metadata point to replace;

  • sample: name of the sample that corresponds to the metadata point to replace;

  • new.value: new value (numeric or character string) of the metadata point.

8 Univariate Analysis

8.1 T-test

8.1.1 Function to use

tTests_dataset(dataset, metadata.var, threshold = NULL, write.file = F, file.out = “ttests.csv”)

  • dataset: a specmine dataset;

  • metadata.var: metadata variable to use in the t-tests. It is tested the differences in means of the variables between the samples that belong to the different groups of the metadata variable given;

  • threshold: threshold value of the p-value. Only the results of the variables whose p-values are under the threshold are returned; optional

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “ttests.csv”.

plot_ttests(dataset, tt.results, tt.threshold = 0.01)

  • dataset: the specmine dataset that led to the results of the t-tests given in tt.results argument;

  • tt.results: variable containing the t-tests results, obatined from tTests_dataset function;

  • tt.treshold: numeric value indicating the p-value treshold. An horizontal line will be drawn in the plot and the data points whose p-values are under the treshold will be blue, while the other ones will be grey. Defaults to 0.01.

8.1.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Perform t-tests on the variables of the dataset, testing if they are significantly different between the classes of the metadata variable Muscle.loss

res_ttest_concentrations = tTests_dataset(concentrations_dataset, "Muscle.loss")

2. Table with the results obtained:

DT::datatable(res_ttest_concentrations)

3. Plot of the results, with a p-value treshold of 0.05:

plot_ttests(concentrations_dataset, res_ttest_concentrations, tt.threshold = 0.05)

8.2 One-Way ANOVA

8.2.1 Function to use

aov_all_vars(dataset, column.class, doTukey = T, write.file = F, file.out = “anova-res.csv”)

  • dataset: a specmine dataset;

  • column.class: metadata variable to use in the ANOVA. It is tested the differences in means of the variables between the samples that belong to the different groups of the metadata variable given;

  • doTukey: boolean value (TRUE or FALSE) indicating if TukeyHSD test should be performed or not. Defaults to TRUE;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defauls to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “anova-res.csv”.

plot_anova(dataset, anova.results, anova.threshold = 0.01, reverse.x = F)

  • dataset: the specmine dataset that led to the results of the t-tests given in anova.results argument;

  • anova.results: variable containing the one-way ANOVA results, obatined from aov_all_vars function;

  • anova.treshold: numeric value indicating he p-value treshold. An horizontal line will be drawn in the plot and the data points whose p-values are under the treshold will be blue, while the other ones will be grey. Defaults to 0.01;

  • reverse.x: boolean value (TRUE or FALSE) indicating if the x-axis should be plotted in reverse or not. Defaults to FALSE.

8.2.2 Examples

Example makes use of an IR dataset (IR dataset used).

1. To perform this test, the original dataset was filtered, using interquantile range function and filtering 75% of the variables:

ir_spectra_dataset_fpf=flat_pattern_filter(ir_spectra_dataset, red.value=75)

2. Perform one-way ANOVA tests on the variables of the dataset, testing if they are significantly different between the classes of the metadata variable varieties, without performing the TukeyHSD test:

res_one_anova_ir = aov_all_vars(ir_spectra_dataset_fpf, "varieties", doTukey = FALSE)

3. Table with the results obtained:

DT::datatable(res_one_anova_ir)

4. Plot of the results, with a treshold of 0.05*10^-8:

plot_anova(ir_spectra_dataset_fpf, res_one_anova_ir, 0.05e-8)

8.3 Multifactor ANOVA

8.3.1 Functions to use

To perform multifactor ANOVA:

multifactor_aov_all_vars(dataset, metadata.vars, combination)

  • dataset: a specmine dataset;

  • metadata.vars: character vector with strings representing the metadata variables to use in multifactor ANOVA. It is tested the differences in means of the variables between the samples that belong to the different groups of the metadata variables given;

  • combination: string representating a formula specifying the model. For example, if metadata.vars is c(“var1”, “var2”), combination could be “var1+var2”.

To get the p-values table from the results obtained by using previous function

multifactor_aov_pvalues_table(multifactor.aov.results, write.file = F, file.out = “multi-anova-pvalues.csv”)

  • multifactor.aov.results: variable containing the multifactor anova results, obtained from using the previous function;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results table to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “multi-anova-pvalues.csv”.

To get the table with the explained variability from the results obtained by using the function multifactor_aov_all_vars

multifactor_aov_varexp_table(multifactor.aov.results, write.file = F, file.out = “multi-anova-varexp.csv”)

  • multifactor.aov.results: variable containing the multifactor anova results, obtained from using the previous function;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results table to be written in a file. Defaults to FAlSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “multi-anova-varexp.csv”.

8.3.2 Examples

Example makes use of an nmr-peaks dataset (NMR peaks dataset used).

1. As the original dataset contains missing values, you must treat the missing values, in order to be able to perform the analysis. The missing values were replaced by the value 0.00005:

nmr_peaks_dataset_mv=missingvalues_imputation(nmr_peaks_dataset)

2. Perform multifactor ANOVA tests on the variables seasons and agroregions of the dataset, by testing if they are significantly different whith the model “seasons*agroregions“:

res_multi_anova_nmr_peaks = multifactor_aov_all_vars(nmr_peaks_dataset_mv, c("seasons","agroregions"), "seasons*agroregions")

3. Table with the p-values of the results:

res_multi_anova_nmr_peaks_p.values=multifactor_aov_pvalues_table(res_multi_anova_nmr_peaks)
DT::datatable(res_multi_anova_nmr_peaks_p.values)

4. Table with the explained variability of the results:

res_multi_anova_nmr_peaks_exp.var=multifactor_aov_varexp_table(res_multi_anova_nmr_peaks)
DT::datatable(res_multi_anova_nmr_peaks_exp.var)

8.4 Kruskal-Wallis Test

8.4.1 Function to use

kruskalTest_dataset(dataset, metadata.var, threshold = NULL, write.file = F, file.out = “kruskal.csv”)

  • dataset: a specmine dataset;

  • metadata.var: string representing the metadata variable to use in Kruskal-wallis tests. It is tested the differences in means of the variables between the samples that belong to the different groups of the metadata variable given;

  • treshold: threshold value of the p-value. Only the results of the variables whose p-values are under the threshold are returned; optional

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “kruskal.csv”.

plot_kruskaltest(dataset, kr.results, kr.threshold = 0.01)

  • dataset: the specmine dataset that led to the results of the kruskall-wallis tests given in kr.results argument;

  • kr.results: variable containing the kruskal-wallis tests result, obatined from kruskalTest_dataset function;

  • kr.treshold: numeric value indicating he p-value treshold. An horizontal line will be drawn in the plot and the data points whose p-values are under the treshold will be blue, while the other ones will be grey. Defaults to 0.01.

8.4.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Perform Kruskal-Wallis tests on the variables of the dataset, testing if they are significantly different between the classes of the metadata variable Muscle.loss. No p-value treshold will be given so that the full results can be seen:

res_kwtest_concentrations = kruskalTest_dataset(concentrations_dataset, "Muscle.loss")

2. Table with the results obtained:

DT::datatable(res_kwtest_concentrations)

3. Plot of the results, with a treshold value of 0.05:

plot_kruskaltest(concentrations_dataset, res_kwtest_concentrations, kr.threshold = 0.05)

8.5 Kolmogorov Smirnov Test

8.5.1 Function to use

ksTest_dataset(dataset, metadata.var, threshold = NULL, write.file = F, file.out = “ks.csv”)

  • dataset: a specmine dataset;

  • metadata.var: string representing the metadata variable to use in Kolmogorov-Smirnov tests. It is tested the differences in means of the variables between the samples that belong to the different groups of the metadata variable given;

  • treshold: threshold value of the p-value. Only the results of the variables whose p-values are under the threshold are returned; optional

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “ks.csv”.

plot_kstest(dataset, ks.results, ks.threshold = 0.01)

  • dataset: the specmine dataset that led to the results of the kolmogorov-smirnov tests given in ks.results argument;

  • ks.results: variable containing the kolmogorov-smirnov tests result, obatined from ksTest_dataset function;

  • ks.treshold: numeric value indicating he p-value treshold. An horizontal line will be drawn in the plot and the data points whose p-values are under the treshold will be blue, while the other ones will be grey. Defaults to 0.01.

8.5.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Perform Kolmogorov-Smirnov tests on the variables of the dataset, testing if they are significantly different between the classes of the metadata variable Muscle.loss. No p-value treshold will be given so that the full results can be seen:

res_kstest_concentrations = ksTest_dataset(concentrations_dataset, "Muscle.loss")
## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

## Warning in ks.test(sub.ds1$data[i, ], sub.ds2$data[i, ]): cannot compute exact p-value with ties

2. Table with the results obtained:

DT::datatable(res_kstest_concentrations)

3. Plot of the results, with a p-value treshold of 0.05:

plot_kstest(concentrations_dataset, res_kstest_concentrations, 0.05)

8.6 Fold Change Analysis

8.6.1 Functions to use

You can perform fold change in each variable of the dataset, i.e., analyse the difference of each variable on two groups

fold_change(dataset, metadata.var, ref.value, threshold.min.fc = NULL, write.file = F, file.out = “fold_change.csv”)

  • dataset: a specmine dataset;

  • metadata.var: string representing the metadata variable to use in the fold change analysis. It is tested the differences of the variables on the chosen metadata variable classest;

  • ref.value: string representing the name of one of the classes of the metadata variable chosen that will be considered the initial value;

  • treshold.min.fc: minimum treshold of the fold change value. Only the results of the groups whose fold change values are above the threshold are returned; optional

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “fold_change.csv”.

You can perform fold change on two variables, i.e., analyse the difference of groups on two variables

fold_change_var(dataset, metadata.var, variables, threshold.min.fc = NULL, write.file = F, file.out = “fold_change_reverse.csv”)

  • dataset: a specmine dataset;

  • metadata.var: string representing the metadata variable to use in the fold change test. It is tested the differences of the classes in the metadata variable on the two variables chosen in variables argument;

  • variables: character vector with the names representing the two variables being tested;

  • treshold.min.fc: minimum treshold of the fold change value. Only the results of the groups whose fold change values are above the threshold are returned; optional

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “fold_change_reverse.csv”.

You can see a plot of the results

plot_fold_change(dataset, fc.results, fc.threshold, plot.log = T, var = F, xlab = “”)

  • dataset: the specmine dataset that led to the results of the fold change analysis given in fc.results argument;

  • fc.results: variable containing the fold change analysis result, obatined from fold_change function;

  • fc.treshold: numeric value indicating he fold change treshold. An horizontal line will be drawn in the plot and the data points whose fold change values are under the treshold will be blue, while the other ones will be grey;

  • plot.log: boolean value (TRUE or FALSE) indicating if the fold change values are transformed logarithmically or not. Defaults to TRUE;

  • var: boolean value (TRUE or FALSE) indicating if the label of the xx axis is given in xlab argument. Defaults to FALSE;

  • xlab: string representing the x axis label. optional

You can see a plot that joins the results from fold change analysis and t-test analysis

volcano_plot_fc_tt(dataset, fc.results, tt.results, fc.threshold = 2, tt.threshold = 0.01)

  • dataset: the specmine dataset that led to the results of the fold change analysis given in fc.results argument and the t-test analysis given in tt.results argument;

  • fc.results: variable containing the fold change analysis result, obatined from fold_change function;

  • tt.results: variable containing the t-test analysis result, obatined from tTests_dataset function;

  • fc.treshold: numeric value indicating he fold change treshold. A vertical line will be drawn in the plot and the data points whose fold change values are under the treshold will be blue, while the other ones will be grey;

  • tt.treshold: numeric value indicating he fold change treshold. An horizontal line will be drawn in the plot and the data points whose fold change values are under the treshold will be blue, while the other ones will be grey;

8.6.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Perform fold change analysis on each variable of the dataset, testing the difference of each data variable in the two classes (control and cachexic) of the metadata variable Muscle.loss. “control” was considered the initial value and no treshold was specified:

res_fc_vars_concentrations = fold_change(concentrations_dataset, "Muscle.loss", ref.value="control")

2. Table with the results obtained:

DT::datatable(res_fc_vars_concentrations)

3. Plot of the results, with a treshold value of 2:

plot_fold_change(concentrations_dataset, res_fc_vars_concentrations, 2)

4. Plot of the fold change and t-test results (from the example for the t-test analysis), with treshold values of 2 and 0.01 respectively:

volcano_plot_fc_tt(concentrations_dataset, res_fc_vars_concentrations, res_ttest_concentrations)

##  [1] "3-Hydroxybutyrate"    "3-Hydroxyisovalerate" "Acetate"              "Alanine"              "Betaine"              "Dimethylamine"       
##  [7] "Glutamine"            "Leucine"              "N.N-Dimethylglycine"  "Pyroglutamate"        "Quinolinate"          "Serine"              
## [13] "Valine"               "cis-Aconitate"        "myo-Inositol"

5. Perform fold change analysis on the differences of the classes of the Muscle.loss metadata variable (“control” and“cachexic”) on the variables “Creatine” and “Serine”. Again, no treshold was specified:

res_fc_2var_concentrations = fold_change_var(concentrations_dataset, "Muscle.loss", c("Creatine", "Serine"))

6. Table with the results obtained:

DT::datatable(res_fc_2var_concentrations)

9 Principal Components Analysis (PCA)

9.1 Functions to use

Classical PCA

pca_analysis_dataset(dataset, scale = T, center = T, write.file = F, file.out = “pca”)

  • dataset: a specmine dataset;

  • scale: boolean value (TRUE or FALSE) indicating whether the variables should be scaled or not;

  • center: boolean value (TRUE or FALSE) indicating whether the variables should be centered or not;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “pca”.

Robust PCA

The difference to the previous method consists on being able to choose how to center and scale the dataset

pca_robust(dataset, center = “median”, scale = “mad”, k = 10, write.file = F, file.out = “robpca”)

  • dataset: a specmine dataset;

  • center: string indicating how variables should be centered. It can either be “median” or “mean”. It can also be a numeric vector with the center values of each column (sample). Defaults to “median”;

  • scale: string indicating how variables should be scaled. It can either be standard deviation ratio (“sd”) or mean absolute deviation (“mad”). It can also be a numeric vector with the scale value of each column (sample). Defaults to Mean absolute deviation (“mad”);

  • k: number of components to compute;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you chosen to write the results to a file (write.file=T). Defaults to “robpca”.

Numerical Results

The following function returns the importance of the principal components:

pca_importance(pca.res, pcs = 1:length(pca.res$sdev), sd = T, prop = T, cumul = T, min.cum = NULL)

  • pca.res: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • pcs: numeric vector indicating for what principal components pca importance should be calculated. Defaults to all components;

  • sd: boolean value (TRUE or FALSE) indicating whether standard deviation of the results should be returned or not. Defaults to TRUE;

  • prop: boolean value (TRUE or FALSE) indicating whether proportion of variance of the results should be returned or not. Defauls to TRUE;

  • cumul: boolean value (TRUE or FALSE) indicating whether the cumulative variance should be returned or not. Defaults to TRUE;

  • min.cum: numeric value, in percentage, indicating the minimum cumulative percentage of variance. optional

9.1.1 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Perform a normal PCA analysis, by scaling and centering the variables:

res_norm_pca_concentrations = pca_analysis_dataset(concentrations_dataset)

2. PCA importance table:

res_imp_pca_concentrations=pca_importance(res_norm_pca_concentrations, pcs=1:5)
DT::datatable(res_imp_pca_concentrations, options=list(scrollX=T))

3. PCA loadings table:

To get loadings values of robust PCA results, instead of ‘rotation’ change to ‘loadings’

DT::datatable(round(res_norm_pca_concentrations$rotation, 4), options=list(scrollX=T))

4. PCA scores table:

To get loadings values of robust PCA results, instead of ‘x’ change to ‘scores’

DT::datatable(round(res_norm_pca_concentrations$x, 4), options=list(scrollX=T))

5. To see other results available, get the names of the result’s list and then access that result:

_For more details on what the results return see the value section of function prcomp for normal PCA results and princomp for robust PCA results, from stats package.

names(res_norm_pca_concentrations)
## [1] "sdev"     "rotation" "center"   "scale"    "x"
res_norm_pca_concentrations$sdev #Standard deviations of the principal components
##  [1] 5.04667 2.27013 1.83311 1.74728 1.65906 1.61305 1.47304 1.36403 1.24275 1.20650 1.15841 1.05503 1.03620 0.99140 0.96773 0.89551 0.86788 0.83041
## [19] 0.81327 0.73918 0.72112 0.71053 0.64606 0.63389 0.58304 0.54425 0.50539 0.48743 0.42674 0.42427 0.41483 0.38653 0.35092 0.32424 0.31646 0.28671
## [37] 0.28435 0.26060 0.25353 0.24800 0.21896 0.19537 0.18914 0.17667 0.16864 0.15799 0.15287 0.13804 0.13101 0.10759 0.10374 0.09853 0.08760 0.08258
## [55] 0.08049 0.06927 0.05937 0.05673 0.05088 0.04001 0.02972 0.02789 0.01876

9.2 Plot functions and examples

Examples for each plot will use the pca results from the example above

9.2.1 Scree plot

Shows the individual and cumulative percentages of the explained variance of each principal component

pca_screeplot(pca.result, num.pcs = NULL, cex.leg = 0.8, leg.pos = “right”, lab.text = c(“individual percent”, “cumulative percent”), fill.col = c(“blue”, “red”), ylab = “Percentage”, xlab = “Principal components”, …)

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • num.pcs: number of principal components to show in the plot. If NULL, shows all components. Defaults to NULL;

  • cex.leg: number indicating the relative font size of legend. Defaults to 0.8;

  • leg.pos: string indicating the position of the legend in the plot. It can either be “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”. Defaults to “right”;

  • lab.text: character vector with the labels of the legend. Defaults to c(“individual percent”, “cumulative percent”);

  • fill.col: character vector with the colors of each line. Defaults to c(“blue”, “red”). Names of the available colors can be consulted using the function [colors]](https://www.rdocumentation.org/packages/grDevices/versions/3.4.3/topics/colors) from the grDevices package;

  • ylab: yy axis label. Defaults to “Percentage”;

  • xlab: xx axis label. Defaults to “Principal components”;

  • : aditional parameters of matplot funtion.

9.2.1.1 Examples

1. Plot showing results on all principal components, where the blue line represents the individual percent and the red one the cumulative percent:

pca_screeplot(res_norm_pca_concentrations)

9.2.2 Scores plot 2D

Shows a 2D PCA scores plot of two principal components

pca_scoresplot2D(dataset, pca.result, column.class, pcas = c(1, 2), labels = FALSE, ellipses = FALSE, bw=F, pallette = 2, leg.pos = “right”, xlim = NULL, ylim = NULL)

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • column.class: string representing the name of a metadata variable. The plot is colored by the classes of the metadata variable mentioned in this argument;

  • pcas: numeric vector with the two principal components to plot. Defaults to c(1,2);

  • labels: boolean value (TRUE or FALSE) indicating whether samples’ names should be shown in the plot (each point in the plot corresponds to a sample) or not. Defaults to FALSE;

  • ellipses: boolean value (TRUE or FALSE) indicating whether ellipses should be drawn on each class of the metadata variable chosen. Defaults to FALSE. Ellipses are never drawn when bw argument is TRUE;

  • bw: boolean value (TRUE or FALSE) indicating whether the plot should black and white or colored. If bw=TRUE, the plot is black and white. Defaults to FALSE;

  • pallette: pallette of colors by which to color the plot (one different color will be assign to each metadata class). Can be a number or a string indicating the color palette wanted. Examples of color palettes and respective names (from RColorBrewer package):

RColorBrewer::display.brewer.all()

  • leg.pos: string indicating the position of the legend in the plot. It can either be “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”. Defaults to “right”;

  • xlim: numeric vector indicating the limits of the xx axis. optional

  • ylim: numeric vector indicating the limits of the yy axis. optional

9.2.2.1 Examples

1. Colored plot showing results on the first two principal components, with ellipses drawn:

pca_scoresplot2D(concentrations_dataset, res_norm_pca_concentrations, "Muscle.loss", pcas = c(1,2), ellipses = TRUE, pallette = "Paired")

2. Same plot, but black and white:

pca_scoresplot2D(concentrations_dataset, res_norm_pca_concentrations, "Muscle.loss", bw=TRUE)

9.2.3 Normal Scores plot 3D

pca_scoresplot3D(dataset, pca.result, column.class, pcas = c(1, 2, 3))

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • column.class: string representing the name of a metadata variable. The plot is colored by the classes of the metadata variable mentioned in this argument;

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3).

9.2.3.1 Examples

1. Plot showing results on the first three principal components, colored by the classes of the metadata variable Muscle.loss:

pca_scoresplot3D(concentrations_dataset, res_norm_pca_concentrations, "Muscle.loss", pcas = c(1,2,3))

9.2.4 Interactive Scores plot 3D

This function uses the rgl package. If having problems, go to section Problems running code: solutions

pca_scoresplot3D_rgl(dataset, pca.result, column.class, pcas = c(1, 2, 3), size = 1, labels = FALSE)

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • column.class: string representing the name of a metadata variable. The plot is colored by the classes of the metadata variable mentioned in this argument;

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3);

  • size: value indicating the size of the points. Defaults to 1;

  • labels: boolean value (TRUE or FALSE) indicating whether samples’ names should be shown in the plot (each point in the plot corresponds to a sample) or not. Defaults to FALSE.

9.2.4.1 Examples

1. Plot showing results on the first 3 principal components, where each sample is labeled:

When running this, a plot in an interactive rgl window is shown

pca_scoresplot3D_rgl(concentrations_dataset, res_norm_pca_concentrations, "Muscle.loss", labels = TRUE)

9.2.5 Biplot 2D

pca_biplot(dataset, pca.result, cex = 0.8, legend.cex = 0.8, x.colors = 1, inset = c(0, 0), legend.place = “topright”, …)

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • cex: value of the relative font size of legend. Defaults to 0.8;

  • colors:

  • inset: argument of the legend function.

  • legend.place: string indicating the position of the legend in the plot. It can either be “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”. Defaults to “topright”;

  • : additional parameters for biplot function.

9.2.5.1 Examples

1. Biplot of the first two principal components, colored by the classes in the metadata variable Muscle.loss:

pca_biplot(concentrations_dataset, res_norm_pca_concentrations, colors="Muscle.loss")

9.2.6 Interactive Biplot 3D

This function uses the rgl package. If having problems, go to section Problems running code: solutions

pca_biplot3D(dataset, pca.result, column.class = NULL, pcas = c(1, 2, 3))

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • column.class: string representing the name of a metadata variable. The plot is colored by the classes of the metadata variable mentioned in this argument; optional

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3).

9.2.6.1 Examples

1. Biplot of the first three principal components, colored by the classes in the metadata variable Muscle.loss:

When running this, a plot in an interactive rgl window is shown

pca_biplot3D(concentrations_dataset, res_norm_pca_concentrations, "Muscle.loss", pcas = c(1,2,3))

9.2.7 Pairs Plot

pca_pairs_plot(dataset, pca.result, column.class = NULL, pcas = c(1, 2, 3, 4, 5), …)

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • column.class: string representing the name of a metadata variable. The plot is colored by the classes of the metadata variable mentioned in this argument; optional

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3);

  • : additional parameters to ggpairs function.

9.2.7.1 Examples

1. Pairs plot for the first three principal components, colored by the classes in the metadata variable Muscle.loss:

pca_pairs_plot(concentrations_dataset, res_norm_pca_concentrations, "Muscle.loss", pcas = c(1,2,3))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

9.2.8 Kmeans Plot 2D

pca_kmeans_plot2D(dataset, pca.result, num.clusters = 3, pcas = c(1, 2), kmeans.result = NULL, labels = FALSE, bw=F, ellipses = FALSE, leg.pos = “right”, xlim = NULL, ylim = NULL)

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • num.clusters: value indicating the number of clusters to set to when performing the k-means. Only relevant if kmeans.result=NULL;

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3);

  • kmeans.result: variable containing a k-means result. If not given (kmeans.result=NULL), k-means is performed by the function;

  • labels: boolean value (TRUE or FALSE) indicating whether samples’ names should be shown in the plot (each point in the plot corresponds to a sample) or not. Defaults to FALSE;

  • ellipses: boolean value (TRUE or FALSE) indicating whether ellipses should be drawn on each class of the metadata variable chosen. Defaults to FALSE. Ellipses are never drawn when bw argument is TRUE;

  • bw: boolean value (TRUE or FALSE) indicating whether the plot should black and white or colored. If bw=TRUE, the plot is black and white. Defaults to FALSE;

  • leg.pos: string indicating the position of the legend in the plot. It can either be “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”. Defaults to “right”;

  • xlim: numeric vector indicating the limits of the xx axis. optional

  • ylim: numeric vector indicating the limits of the yy axis. optional

9.2.8.1 Examples

1. Kmeans plot for the first two components, where a k-means of two clusters is performed:

pca_kmeans_plot2D(concentrations_dataset, res_norm_pca_concentrations, num.clusters = 2)

9.2.9 Kmeans Plot 3D

This function uses the rgl package. If having problems, go to section Problems running code: solutions

pca_kmeans_plot3D(dataset, pca.result, num.clusters = 3, pcas = c(1, 2, 3), kmeans.result = NULL, labels = FALSE, size = 1, …)

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • num.clusters: value indicating the number of clusters to set to when performing the k-means. Only relevant if kmeans.result=NULL;

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3);

  • kmeans.result: variable containing a k-means result. If not given (kmeans.result=NULL), k-means is performed by the function;

  • labels: boolean value (TRUE or FALSE) indicating whether samples’ names should be shown in the plot (each point in the plot corresponds to a sample) or not. Defaults to FALSE;

  • size: value indicating the size of the points. Defaults to 1.

9.2.9.1 Examples

1. Kmeans plot for the first three components, where a k-means of two clusters is performed:

When running this, a plot in an interactive rgl window is shown

pca_kmeans_plot3D(concentrations_dataset, res_norm_pca_concentrations, num.clusters = 2, pcas = c(1,2,3))

9.2.10 Kmeans Pairs Plot

pca_pairs_kmeans_plot(dataset, pca.result, num.clusters = 3, kmeans.result = NULL, pcas = c(1, 2, 3, 4, 5))

  • dataset: the specmine dataset that led to the pca results given in pca.result argument;

  • pca.result: variable containing the PCA results, obtained from pca_analysis_dataset or pca_robust function;

  • num.clusters: value indicating the number of clusters to set to when performing the k-means. Only relevant if kmeans.result=NULL;

  • kmeans.result: variable containing a k-means result. If not given (kmeans.result=NULL), k-means is performed by the function;

  • pcas: numeric vector with the three principal components to plot. Defaults to c(1,2,3).

9.2.10.1 Examples

1. Kmeans plot for the first three components, where a k-means of two clusters is performed:

pca_pairs_kmeans_plot(concentrations_dataset, res_norm_pca_concentrations, num.clusters = 2, pcas = c(1,2,3,4,5))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

10 Clustering Analysis

10.1 Function to use

clustering(dataset, method = “hc”, distance = “euclidean”, type = “samples”, num.clusters = 5, clustMethod = “complete”)

  • dataset: a specmine dataset;

  • method: a string representing the clustering method. It can either be:

    • “hc”: Hierarchical Clustering. Default value;

    • “kmeans”: K-Means Clustering

  • distance: only necessary for hierarchical clustering, a string representing the distance measure to be used when computing the distances between the samples or variables. It can either be “euclidean”, “manhattan”, “pearson” or “spearman”. Defaults to “euclidean”;

  • type: a string representing if the cluster analysis should be performed on samples (“samples”) or on variables (“variables”). Defaults to “samples”;

  • num.clusters: only necessary for k-means clustering, a numeric value indicating the number of clusters in the k-means analysis. Defaults to 5;

  • clustMethod: only necessary for hierarchical clustering, a string representing the agglomeration method. It can either be “complete”, “ward”, “single”, “average”, “mcquitty”, “median” or “centroid”. Defaults to “complete”.

10.2 Hierarchical Clustering

10.2.1 Results functions

dendrogram_plot_col(dataset, hc.result, classes.col, colors = NULL, title = “”, lab.cex = 1, leg.pos = “topright”, label_samples=NULL)

  • dataset: the specmine dataset that led to the results of hierarchical clustering given in hc.result argument;

  • hc.clust: variable containing the hierarchical clustering result, obtained from clustering function;

  • classes.col: string or index representing the metadata variable by which the leafs will be colored;

  • colors: vector with the different colors for each metadata class in the metadata variable chosen in classes.col argument. If nothing is given (colors=NULL), the colors will be generated automatically. Defaults to NULL;

  • title: string representing the title that should appear at the top of the plot; optional

  • lab.cex: numeric value representing the realtive size of the labels’ text. Defaults to 1;

  • leg.pos: string representing the position of the legend in the plot. It can either be “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”. Defaults to “right”;

  • label_samples: string or index representing the metadata variable by which the leafs will be named. If not given (label_samples=NULL), the leafs are named with the respective samples names. Defaults to NULL.

10.2.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Hierarchical clustering of the samples, using the euclidean distance and the agglomeration method complete:

res_hclust_concentrations = clustering(concentrations_dataset)

2. Dendogram plot, where samples are colored by the metadata variable Muscle.loss:

dendrogram_plot_col(concentrations_dataset, res_hclust_concentrations, "Muscle.loss", title = "Dendogram plot for concentrations dataset")

3. The hierarchical clustering result obtained is a list with the following components:

To know more about the returned results for a hierarchical clustering, see the information on the function hclust.

names(res_hclust_concentrations)
## [1] "merge"       "height"      "order"       "labels"      "method"      "call"        "dist.method"

10.3 K-Means Clustering

10.3.1 Results functions

You can visualize a k-means plot for each cluster

kmeans_plot(dataset, kmeans.result)

  • dataset: the specmine dataset that led to the results of k-means clustering given in hc.result argument;

  • kmeans.result: variable containing the k-means clustering result, obtained from clustering function;

You can see a dataframe with the samples that belong to each cluster

kmeans_result_df(kmeans.result)

  • kmeans.result: variable containing the k-means clustering result, obtained from clustering function;

10.3.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Hierarchical clustering of the samples, using the euclidean distance and the agglomeration method complete:

res_kclust_concentrations = clustering(concentrations_dataset, method="kmeans", num.clusters=2)

2. K-means results plot, where the grey represents the values of all samples of that cluster and blue the median of those samples:

kmeans_plot(concentrations_dataset, res_kclust_concentrations)

3. K-means results data frame:

DT::datatable(kmeans_result_df(res_kclust_concentrations), options=list(scrollX=T), rownames=F)

4. The k-means clustering result obtained is a list with the following components:

To know more about the returned results for a hierarchical clustering, see the information on the function kmeans.

names(res_kclust_concentrations)
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"         "iter"         "ifault"

11 Machine Learning

Machine learning in this package is only available for classification problems. Try to convert the numeric metadata to factors so that you can use them for machine learning

11.1 Train Models

train_models_performance(dataset, models, column.class, validation, num.folds = 10, num.repeats = 10, tunelength = 10, tunegrid = NULL, metric = NULL, compute.varimp = T)

  • dataset: a specmine dataset;

  • models: character vector indicating the models to train. The models that can be used are the ones available for the caret package. A list with the available models can be accessed here. The names to use in this argument that represent the model wanted are in the column “method Value”. Only classification models can be used (“Type” column);

  • column.class: string representing the metadata variable to predict;

  • validation: string representing the validation method. It can either be:

    • “boot”: Resampling;

    • “cv”: Cross-Validation;

    • “repeatedcv”: Repeated Cross-validation;

    • “loocv”: Leave One Out Cross-Validation;

    • “lgocv”: Leave Group Out Cross-Validation.

  • num.folds: if a cross validation method is chosen, a numeric value with the number of folds must be given. Defaults to 10;

  • num.repeats: if “boot” or “repeatedcv” are chosen, a numeric value with the number of repeats must be given. Defaults to 10;

  • tunelength: numeric value indicating the number of different values for each parameter of the model(s) chosen should be tested, to find the best one among them. Defaults to 10;

  • tunegrid: list of dataframe(s) with possible tuning values for each model to train. See examples below for more details (step 8);

  • metric: string indicating the metric to use when evaluating the model’s performance. It can either be “Accuracy” or “ROC”;

  • compute.varimp: boolean value (TRUE or FALSE) indicating whether variable importance should be calculated (compute.varimp=TRUE) or not (compute.varimp=FALSE). Defaults to TRUE.

3D Plot for a final PLS model trained

pca_plot_3d(dataset, model, var.class, pcas = 1:3, colors = NULL, legend.place = “topright”, …)

  • dataset: the specmine dataset that led to the final PLS model trained given in model argument;

  • model: variable containing a pls final model, obtained from the results of train_models_performance function, in ‘results\(final.model\)pls’;

  • var.class: string representing the name of the metadata variable by which to color the plot data points;

  • pcas: numeric vector with the 3 components to plot. Defaults to c(1,2,3), the first three components of the model;

  • colors: character vector with the names of the colors that will represent each class of the metadata variable;

  • legend.place: string indicating the position of the legend in the plot. It can either be “bottomright”, “bottom”, “bottomleft”, “left”, “topleft”, “top”, “topright”, “right” or “center”. Defaults to “topright”;

  • : additional parameters of legend function.

11.1.1 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Training model PLS (“pls”) and Random Forests (“rf”) to predict the metadata variable Muscle.loss (i.e., if the sample belongs to cachexic or control). For model validation, it was here used the cross validation method (“cv”), with 10 folds. For parameter optimization, it was set to test 10 different values of each parameter of the two models chosen:

res_train_concentrations=train_models_performance(concentrations_dataset, c("pls", "rf"), "Muscle.loss", "cv", metric="Accuracy")
## Warning: `repeats` has no meaning for this resampling method.
## Loading required package: lattice
## Loading required package: ggplot2
## 
## Attaching package: 'caret'
## The following object is masked from 'package:specmine':
## 
##     multiClassSummary
## 
## Attaching package: 'pls'
## The following object is masked from 'package:caret':
## 
##     R2
## The following object is masked from 'package:stats':
## 
##     loadings
## Warning: `repeats` has no meaning for this resampling method.

2. Table with the performances of each best model trained:

DT::datatable(res_train_concentrations$performance)

3. The parameters of each best model trained:

  • PLS model
DT::datatable(res_train_concentrations$best.tunes$pls)
  • Random Forest model
DT::datatable(res_train_concentrations$best.tunes$rf)

4. Tables with the variable importance for each model:

  • PLS model
DT::datatable(res_train_concentrations$vips$pls)
  • Random Forest model
DT::datatable(res_train_concentrations$vips$rf)

5. Results for each value of the parameters tested:

  • PLS model
DT::datatable(res_train_concentrations$full.results$pls)
  • Random Forest model
DT::datatable(res_train_concentrations$full.results$rf)

6. Confusion matrices for each model trained:

  • PLS model
res_train_concentrations$confusion.matrices$pls
## Cross-Validated (10 fold) Confusion Matrix 
## 
## (entries are percentual average cell counts across resamples)
##  
##           Reference
## Prediction cachexic control
##   cachexic     49.4    16.9
##   control      11.7    22.1
##                             
##  Accuracy (average) : 0.7143
  • Random Forest model
res_train_concentrations$confusion.matrices$rf
## Cross-Validated (10 fold) Confusion Matrix 
## 
## (entries are percentual average cell counts across resamples)
##  
##           Reference
## Prediction cachexic control
##   cachexic     49.4    15.6
##   control      11.7    23.4
##                             
##  Accuracy (average) : 0.7273

7. Each final model can be accessed through res_train_concentrations$final.models$pls for the PLS model and res_train_concentrations$final.models$rf for the Random Forests model. It is with these final models that the user can predict new samples.

8. If you want to provide specific parameter values to test, this is how you use the tungrid argument. You will have to consult the documentation in caret package to know what are the parameters than can be tested for each model.

  • Here, it is present an example for the most commonly used models on how to create the dataframe of tuning parameters for each one of them, using expand.grid function:
##VALUES HERE INTRODUCED FOR PARAMETER OPTIMIZATION ARE ONLY FOR EXPLAINING PURPOSES, THEY MIGHT NOT BE THE BEST VALUES FOR YOU TO USE IN TESTING###

##For PLS model - "pls":
#####This models only has one parameter - ncomp
nComp=c(1,2,3,4,5,6) #Here we are saying that we want to test the values from 1 to 6 for this parameter
pls_tuningValues_dataframe=expand.grid(ncomp=nComp)

##For DECISION TREE (C4.5-like Trees) model - "J48":
#####This model has two parameters - M and C
m=c(1,2,3)
C=c(1,2,3,4)
J48_tuningValues_dataframe=expand.grid(M=m, C=C)
  
##For RULE BASED CLASSIFIER model - "JRip":
#####This model has theree parameters - NumOpts, NumFolds and MinWeights
numopts=c(1,2,3)
numfolds=c(1,2,3)
minweights=c(1,2,3)
JRip_tuningValues_dataframe=expand.grid(NumOpt=numopts, NumFolds=numfolds, MinWeights=minweights)

##For SVM LINEAR model - "svmLinear":
#####This model has one parameter - cost
cost=c(1,4,6)
svmLinear_tuningValues_dataframe=expand.grid(cost=cost)

##For RANDOM FORESTS model - "rf":
#####This model has one parameter - mtry
mtry=c(5,7,8)
rf_tuningValues_dataframe=expand.grid(mtry=mtry)

##For NEURAL NETWORKS model - "nnet":
#####This model has two parameters - size and decay
size=c(3,4,5)
decay=c(1,2)
nnet_tuningValues_dataframe=expand.grid(size=size, decay=decay)
  • Now, if wanted to train PLS and Random Forests models, like the example above, but using specific parameters to test, you should perform as follows:
#Creating the list of dataframes of tuning parameters
tuneList=list()
tuneList[["pls"]]=pls_tuningValues_dataframe
tuneList[["rf"]]=rf_tuningValues_dataframe

res_train_concentrations=train_models_performance(concentrations_dataset, c("pls", "rf"), "Muscle.loss", "cv", metric="Accuracy", tunegrid=tuneList)
## Warning: `repeats` has no meaning for this resampling method.

## Warning: `repeats` has no meaning for this resampling method.
  • As you can see, now the results for each value of the parameters tested are:

PLS model

DT::datatable(res_train_concentrations$full.results$pls)

Random Forest model

DT::datatable(res_train_concentrations$full.results$rf)

11.2 Predict new samples

predict_samples(train.result, new.samples)

  • train.result: variable containing a final model obtained from the function train_models_performance;

  • new.samples: the data matrix from a specmine dataset, where it will be predicted, for each sample, the value of the metadata variable trained to predict in the model given in train.result argument.

11.2.1 Example

Example makes use of an concentrations dataset (concentrations dataset used) and the results obtained in the example for the function train_models_performance.

As we do not have samples whose output is unkown for these or other datasets here being used, we will here use the same data used to train the models (only for explaining purposes).

1. Here, we use the PLS model to predict the ouput of the samples given:

res_predict_concentrations=predict_samples(res_train_concentrations$final.models$pls, concentrations_dataset$data)

2. Table with the results. The first column contains the name of each sample predicted and the second column the output value given:

DT::datatable(res_predict_concentrations)

11.3 Train and Predict

you can train models and predict new samples in only one function

train_and_predict(dataset, new.samples, column.class, model, validation, num.folds = 10, num.repeats = 10, tunelength = 10, tunegrid = NULL, metric = NULL)

  • dataset: a specmine dataset;

  • new.samples: a specmine dataset, where it will be predicted, for each sample, the value of the metadata variable trained to predict in the model given in model argument;

  • column.class: string representing the metadata variable to predict;

  • model: string representing the model to train. The models that can be used are the ones available for the caret package. A list with the available models can be accessed here. The names to use in this argument that represent the model wanted are in the column “method Value”. Only classification models can be used (“Type” column);

  • validation: string representing the validation method. It can either be:

    • “boot”: Resampling;

    • “cv”: Cross-Validation;

    • “repeatedcv”: Repeated Cross-validation;

    • “loocv”: Leave One Out Cross-Validation;

    • “lgocv”: Leave Group Out Cross-Validation.

  • num.folds: if a cross validation method is chosen, a numeric value with the number of folds must be given. Defaults to 10;

  • num.repeats: if “boot” or “repeatedcv” are chosen, a numeric value with the number of repeats must be given. Defaults to 10;

  • tunelength: numeric value indicating the number of different values for each parameter of the model(s) chosen should be tested, to find the best one among them. Defaults to 10;

  • tunegrid: list of dataframe(s) with possible tuning values for each model to train. See examples below for more details (step 8);

  • metric: string indicating the metric to use when evaluating the model’s performance. It can either be “Accuracy” or “ROC”.

12 Feature Selection

12.1 Function to use

feature_selection(dataset, column.class, method = “rfe”, functions, validation = “cv”, repeats = 5, number = 10, subsets = 2^(2:4))

  • dataset: a specmine dataset;

  • column.class: string representing the metadata variable to predict;

  • method: string representing the feature selection method to use. It can either be Recursive Feature Elimination (“rfe”) or Selection by filter (“filter”). Defaults to “rfe”;

  • functions: function to use in model fitting prediction and variable importance/filtering. It can eitherbe:

    • Random Forests: functions=caret::rfFuncs, if method="rfe", or functions=caret::rfSBF, if method="filter";

    • Linear Regression: functions=caret::lmFuncs, if method="rfe", or functions=caret::lmSBF, if method="filter";

    • Bagged Trees: functions=caret::treebagFuncs, if method="rfe", or functions=caret::treebagSBF, if method="filter";

    • Linear Discriminant Analysis (LDA): functions=caret::ldaFuncs, if method="rfe", or functions=caret::ldaSBF, if method="filter";

    • Naive-Bayes: functions=caret::nbFuncs, if method="rfe", or functions=caret::nbSBF, if method="filter".

  • validation: string representing the validation method. It can either be:

    • “boot”: Resampling;

    • “cv”: Cross-Validation;

    • “repeatedcv”: Repeated Cross-validation;

    • “loocv”: Leave One Out Cross-Validation;

    • “lgocv”: Leave Group Out Cross-Validation.

  • repeats: if “boot” or “repeatedcv” are chosen, a numeric value with the number of repeats must be given. Defaults to 5;

  • number: if a cross validation method is chosen, a numeric value with the number of folds must be given. Defaults to 10;

  • subsets: numeric vector indicating the number of features for each group of test. Only necessary to indicate when method=“rfe”. Defaults to 2^(2:4).

12.2 Example

Example makes use of an concentrations dataset (concentrations dataset used).

1. Recursive Feature Elimination, where the function chosen to do model fitting prediction and variable importance/filtering is rfFuncs (random forests). For model validation, the cross validation method with 3 folds was used:

res_fs_rfe_concentrations=feature_selection(concentrations_dataset, "Muscle.loss", method="rfe", functions = caret::rfFuncs, validation = "cv", number = 3, subsets = 2^(1:6))

2. Summary of the results:

res_fs_rfe_concentrations
## 
## Recursive feature selection
## 
## Outer resampling method: Cross-Validated (3 fold) 
## 
## Resampling performance over subset size:
## 
##  Variables Accuracy Kappa AccuracySD KappaSD Selected
##          2    0.662 0.281     0.0271  0.0437         
##          4    0.649 0.265     0.0715  0.1531         
##          8    0.714 0.386     0.0484  0.0820         
##         16    0.715 0.395     0.0548  0.1026         
##         32    0.714 0.392     0.0484  0.0930         
##         63    0.741 0.450     0.0788  0.1545        *
## 
## The top 5 variables (out of 63):
##    X3.Hydroxyisovalerate, Creatine, Methylamine, myo.Inositol, Glucose

3. Name of the variables that make the best group:

res_fs_rfe_concentrations$optVariables
##  [1] "X3.Hydroxyisovalerate"       "Creatine"                    "Methylamine"                 "myo.Inositol"               
##  [5] "Glucose"                     "Adipate"                     "Quinolinate"                 "Glutamine"                  
##  [9] "Pyroglutamate"               "N.N.Dimethylglycine"         "Sucrose"                     "Histidine"                  
## [13] "Trimethylamine.N.oxide"      "Formate"                     "Tryptophan"                  "Betaine"                    
## [17] "Leucine"                     "Acetate"                     "Carnitine"                   "Succinate"                  
## [21] "Creatinine"                  "Lysine"                      "Xylose"                      "Valine"                     
## [25] "Asparagine"                  "Threonine"                   "Lactate"                     "Hippurate"                  
## [29] "Serine"                      "Dimethylamine"               "cis.Aconitate"               "X2.Aminobutyrate"           
## [33] "X3.Indoxylsulfate"           "pi.Methylhistidine"          "tau.Methylhistidine"         "Pyruvate"                   
## [37] "Guanidoacetate"              "O.Acetylcarnitine"           "X3.Aminoisobutyrate"         "X3.Hydroxybutyrate"         
## [41] "Alanine"                     "Fumarate"                    "X1.6.Anhydro.beta.D.glucose" "Acetone"                    
## [45] "Tyrosine"                    "X2.Oxoglutarate"             "Isoleucine"                  "Trigonelline"               
## [49] "Ethanolamine"                "Fucose"                      "Citrate"                     "Uracil"                     
## [53] "Tartrate"                    "Methylguanidine"             "Glycolate"                   "X2.Hydroxyisobutyrate"      
## [57] "trans.Aconitate"             "X4.Hydroxyphenylacetate"     "Hypoxanthine"                "Pantothenate"               
## [61] "X1.Methylnicotinamide"       "Taurine"                     "Glycine"

4. Performance plot:

plot(res_fs_rfe_concentrations, type=c("g","o"), main="Performance Profile across the different subset sizes")

13 Regression Analysis

13.1 Functions to use

13.1.1 To run analysis

You can perform linear regression in one specific data variable

linregression_onevar(dataset, x.val, metadata.vars, combination)

  • dataset: a specmine dataset;

  • x.val: string representing the variable to be tested in the linear regression analysis;

  • metadata.vars: string or character vector indicating the metadata variable(s) to use in the linear regression;

  • combination: string representing a formula specifying the model. For example, if metadata.vars is c(“var1”, “var2”), combination could be “var1+var2”.

You can also perform linear regression in each data variable at once

linreg_all_vars(dataset, metadata.vars, combination)

  • dataset: a specmine dataset;

  • metadata.vars: string or character vector indicating the metadata variable(s) to use in the linear regression;

  • combination: string representing a formula specifying the model. For example, if metadata.vars is c(“var1”, “var2”), combination could be “var1+var2”.

13.1.2 To visualize results

These functions only can be used for results from the function linreg_all_vars.

You can see a dataframe with the coefficient values

linreg_coef_table(linreg.results, write.file = F, file.out = “linreg-coefs.csv”)

  • linreg.results: variable containing the linear regression results, obtained from linreg_all_vars function;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you have chosen to write the results to a file (write.file=T). Defaults to “linreg-coefs.csv”.

You can see a dataframe with the p-values

linreg_pvalue_table(linreg.results, write.file = F, file.out = “linreg-pvalues.csv”)

  • linreg.results: variable containing the linear regression results, obtained from linreg_all_vars function;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you have chosen to write the results to a file (write.file=T). Defaults to “linreg-pvalues.csv”.

You can see a dataframe with the r-squared values

linreg_rsquared(linreg.results, write.file = F, file.out = “linreg-rsquared.csv”)

  • linreg.results: variable containing the linear regression results, obtained from linreg_all_vars function;

  • write.file: boolean value (TRUE or FALSE) indicating if you want the results to be written in a file. Defaults to FALSE;

  • file.out: string with the file path to write to. You only need to give this information if you have chosen to write the results to a file (write.file=T). Defaults to “linreg-rsquared.csv”.

You can see a plot of the coefficient and p-values

plot_regression_coefs_pvalues(linreg.results, bar.col = NULL, coef.size = 5, …)

  • linreg.results: variable containing the linear regression results, obtained from linreg_all_vars function;

  • bar.col: character vector representing the colors of the bars in the plot. One color for each variable represented; optional

  • coef.size: numeric value indicating the relative size of the font of coefficient values;

  • : additional parameters for function geom_bar.

13.2 Examples

Example makes use of an nmr-peaks dataset (NMR peaks dataset used), already treated for missing values in a previous example.

1. Linear regression on all variables:

res_linreg_nmr_peaks=linreg_all_vars(nmr_peaks_dataset_mv, c("agroregions", "seasons"), "agroregions*seasons")

2. Table with the coefficient values for each combination “agroregions*seasons" of two variables:

DT::datatable(linreg_coef_table(res_linreg_nmr_peaks), options=list(scrollX=T))

3. Table with the p-values for each combination “agroregions*seasons" of two variables:

DT::datatable(linreg_pvalue_table(res_linreg_nmr_peaks), options=list(scrollX=T))

4. Table with the r-squared values for each combination “agroregions*seasons" of two variables:

DT::datatable(linreg_rsquared(res_linreg_nmr_peaks), options=list(scrollX=T))

5. Plot of the coefficient and p-values of the variables 0.98 and 3.81 in the results:

plot_regression_coefs_pvalues(res_linreg_nmr_peaks[c("0.98", "3.81")], bar.col=c("blue", "cyan"))

14 Correlation Analysis

14.1 Functions to use

You can perform a correlations test on two variables or samples from the dataset

correlation_test(dataset, x, y, method = “pearson”, alternative = “two.sided”, by.var = T)

  • dataset a specmine dataset;

  • x: one of the two variables/ samples that will be used in the test;

  • y: the other variable/ sample that will be used in the test;

  • method: Correlation method. It can either be “pearson”, “kendall” or “spearman”. Defaults to “pearson”;

  • alternative: string representing the alternative hypothesis (H1). It can either be:

    • Different from the null hypothesis (H0: x=a and H1: x!=a):“two.sided”;

    • Greater than the null hypothesis (H0: x=a and H1: x>a): “greater”;

    • Less than the null hypothesis (H0: x=a and H1: x<a): “less”;

  • by.var: boolean value (TRUE or FAlSE) indicating whether the correlations test is being performed to variables (by.var=TRUE) or to samples (by.var=FALSE). Defaults to TRUE.

You can perform correlations test between all variables or samples from the dataset

correlations_test(dataset, method = “pearson”, by.var = T, alternative = “two.sided”)

  • dataset: a specmine dataset;

  • method: Correlation method. It can either be “pearson”, “kendall” or “spearman”. Defaults to “pearson”;

  • by.var: boolean value (TRUE or FAlSE) indicating whether the correlations test is being performed to variables (by.var=TRUE) or to samples (by.var=FALSE). Defaults to TRUE;

  • alternative: string representing the alternative hypothesis (H1). It can either be:

    • Different from the null hypothesis (H0: x=a and H1: x!=a):“two.sided”;

    • Greater than the null hypothesis (H0: x=a and H1: x>a): “greater”;

    • Less than the null hypothesis (H0: x=a and H1: x<a): “less”.

You can also calculate the correlations between all variables or samples from the dataset

correlations_dataset(dataset, method = “pearson”, by.var = T)

  • dataset: a specmine dataset;

  • method: Correlation method. It can either be “pearson”, “kendall” or “spearman”. Defaults to “pearson”;

  • by.var: boolean value (TRUE or FAlSE) indicating whether the correlations test is being performed to variables (by.var=TRUE) or to samples (by.var=FALSE). Defaults to TRUE.

You can plot a heatmap with the correlation values obtained from the previous function

heatmap_correlations(correlations, col = NULL, …)

  • correlations: correlations matrix obtained from function correlations_dataset, explained above;

  • col: character vector with the colors to be used on the heatmap. If not given, predefined color pallette will be used; optional

  • : additional arguments to personalize the heatmap. See function heatmap for details.

14.2 Examples

Example makes use of an concentrations dataset (concentrations dataset used).

1. Perform correlations tests on all samples from the dataset, using Pearson method:

res_cor_test_concentrations = correlations_test(concentrations_dataset, by.var = FALSE)

2. Table with the results:

DT::datatable(res_cor_test_concentrations, options=list(scrollX=T))

3. Calculate correlations values between all samples from the dataset:

res_cor_val_concentrations=correlations_dataset(concentrations_dataset, by.var=F)

4. Heatmap of the correlations calculated in step 3.:

Here is shown an example making use of a color pallette, given through the argument col

See pallette argument from pca_scoresplot2D function for details.

colors=rev(colorRampPalette(RColorBrewer::brewer.pal(10, "RdBu"))(256)) #Obtain a character vector with the color from the 'RdBu' color pallette, in reversed manner. It is this character vector that will be given to the col argument

heatmap_correlations(res_cor_val_concentrations, col=colors)

15 Metabolite Identification

Identification of metabolites is only available for LC-MS spectra and NMR Peaks.

15.1 LC-MS Spectra

15.1.1 Function to use

MAIT_identify_metabolites(dataset, metadata.variable, xSet = NULL, data.folder = NULL, features = NULL, mass.tolerance = 0.5)

  • dataset: a specmine dataset;

  • metadata.variable: name of the metadata variable that can help in the identification of the metabolites;

  • xSet: xcmsSet object that can be passed. Defaults to NULL. Normally, it is xSet=dataset$xSet;

  • data.folder: string containing the path of the data folder. Should contain the data read into the specmine dataset given;

  • features: features that can be used to help to identify the metabolites;

  • mass.tolerance: mass tolerance when matching reference metabolites to the dataset.

15.1.2 Example

Example makes use of this LC-MS dataset.

1. Identification of metabolites. The metadata variable that will help on the identification is “type” (in this dataset, it is the only metadata variable available) and all features (variables, i.e., mass/charge ratios) will be used to identify the metabolites:

library(MAIT)
res_id_lcms=MAIT_identify_metabolites(lcms_dataset, "type", features = "all", data.folder=lcms_data_folder, xSet=lcms_dataset$xSet)
## Warning in dir.create(resultsPath): 'Results_MAIT' already exists
## WARNING: No input adduct/fragment table was given. Selecting default MAIT table for positive polarity...
## Set adductTable equal to negAdducts to use the default MAIT table for negative polarity
## Start grouping after retention time.
## Created 134 pseudospectra.
## Spectrum build after retention time done
## Generating peak matrix!
## Run isotope peak annotation
##  % finished: 10  20  30  40  50  60  70  80  90  100  
## Found isotopes: 65 
## Isotope annotation done
## Start grouping after correlation.
## Generating EIC's .. 
## 
## Calculating peak correlations in 134 Groups... 
##  % finished: 10  20  30  40  50  60  70  80  90  100  
## 
## Calculating peak correlations across samples.
##  % finished: 10  20  30  40  50  60  70  80  90  100  
## 
## Calculating isotope assignments in 134 Groups... 
##  % finished: 10  20  30  40  50  60  70  80  90  100  
## Calculating graph cross linking in 134 Groups... 
##  % finished: 10  20  30  40  50  60  70  80  90  100  
## New number of ps-groups:  179 
## xsAnnotate has now 179 groups, instead of 134 
## Spectrum number increased after correlation done
## Generating peak matrix for peak annotation!
## Found and use user-defined ruleset!
## Calculating possible adducts in 179 Groups... 
##  % finished: 10  20  30  40  50  60  70  80  90  100  
## Adduct/fragment annotation done
## 
## Warning in MAIT::peakAnnotation(MAIT.object = mait.object, corrWithSamp = 0.7, : Warning: Folder Results_MAIT/Tables already exists. Possible file
## overwritting.
## Skipping peak aggregation step...
## Warning in MAIT::Biotransformations(MAIT.object = mait.sig, adductAnnotation = T, : No input biotransformations table was given. Selecting default
## MAIT table for biotransformations...
## Warning in MAIT::Biotransformations(MAIT.object = mait.sig, adductAnnotation = T, : No input adduct/fragment table was given. Selecting default MAIT
## table for positive polarity...
## Set adductTable equal to negAdducts to use the default MAIT table for negative polarity
## 
##  % Annotation in progress: 10  20  30  40  50  60  70  80  90  100  WARNING: No input database table was given. Selecting default MAIT database...
## Metabolite identification initiated
## 
##  % Metabolite identification in progress: 0  10  20  30  40  50  60  70  80  90  100  
##  Metabolite identification finished
## Warning in MAIT::identifyMetabolites(MAIT.object = mait.sig, peakTolerance = 0.005): Folder /home/scardoso/Documents/specmine/reports/Results_MAIT/
## Tables already exists. Possible file overwritting.

2. Table with the results:

DT::datatable(res_id_lcms@FeatureInfo@metaboliteTable, options=list(scrollX=T))

3. Table with filtered results. Only known metabolites are here shown, as well only specific columns were selected:

DT::datatable(res_id_lcms@FeatureInfo@metaboliteTable[which(res_id_lcms@FeatureInfo@metaboliteTable$Name!='Unknown'),c(6,9,1,2,3,4,5,7,8,10,11)], options=list(scrollX=T))

15.2 NMR Peaks

15.2.1 Function to use

nmr_identification(dataset, ppm.tol=0.03, clust.method=‘pearson’, clust.treshold=NULL, clust.peaks.min=2, clust.maxPeaks=40, clust.nTop=5, freq=500, nucl=“1H”, solv=NULL, pH=NULL, temp=NULL)

  • dataset: a specmine dataset;

  • ppm.tol: ppm tolerance when matching reference peaks to the dataset peaks;

  • clust.method: correlation method to use in the formation of clusters. Defaults to “pearson”;

  • clust.treshold: minimum correlation between variables to form clusters. If not given (clust.treshold=NULL), the function calculates the optimum value (value that leads to the greater number of clusters);

  • clust.peaks.min: minimum number of variables in each cluster. Only the clusters with at least clust.peaks.min variables will be considered.

  • clust.nTop: number of top metabolites with greater score to show for each cluster;

  • clust.maxPeaks: maximum number of peaks that a cluster can have, while searching for the best correlation value. Defaults to 40, the original value where the code was adapated from. Can also be NULL and this value will be the number of peaks of the larger cluster.

  • freq: frequency of reference spectra, in Hz. Must either be 400, 500 or 600.

  • nucl: atomic nuclei of reference spectra, either “1H” or “13C”.

  • solv: solvent of the sample used to ontain the reference spectra. Possbile values: “100%_DMSO“,”5%_DMSO“,”acetone+DMSO+tetramethylurea“,”acetone+DMSO+tetramethylurea“,”C“,”CCl4“,”CD3OD“,”CDCl3“,”cyclohexane“,”D2O“,”DMSO-d6“,”DMSO-d6+HCl“,”neat“,”TMS“,”Water“. optional

  • pH: pH of the sample used to ontain the reference spectra. Can be a number corresponding to the sample’s pH or a vector of length two indicating a pH interval. optional

  • temp: temperature of the sample used to ontain the reference spectra, in Celsius. Must either be 25 or 50. optional

15.2.2 Example

Example makes use of this NMR peaks dataset.

1. Firstly, to perform metabolite identification, the following pre-processing tasks were applied to the dataset, saving the processed data in a new dataset (nmr_peaks_dataset_processed_ID)

  • Treatment of missing values, as the dataset contains missing values, by replacing them with the value 5*10-4:
count_missing_values(nmr_peaks_dataset)
## [1] 5691
nmr_peaks_dataset_processed_ID=missingvalues_imputation(nmr_peaks_dataset)
  • Logaritmic transformation:
nmr_peaks_dataset_processed_ID=transform_data(nmr_peaks_dataset_processed_ID)
  • Auto scale:
nmr_peaks_dataset_processed_ID=scaling(nmr_peaks_dataset_processed_ID)

2. Now, metabolites will be identified

nmr_ID_res = nmr_identification(nmr_peaks_dataset_processed_ID, clust.maxPeaks = NULL, clust.nTop = 10)
## Getting reference metabolites...
## Getting best correlation value...
## CVAL;nb_clusters;nb_clusters_2;size_max;Criterion;nb_buckets
## 0.9;13;9;76;-15.34;169
## 0.9001;13;9;76;-15.34;169
## 0.9002;13;9;76;-15.34;169
## 0.9003;13;9;75;-15.22;168
## 0.9004;13;9;75;-15.22;168
## 0.9005;13;9;75;-15.22;168
## 0.9006;13;9;75;-15.22;168
## 0.9007;13;9;75;-15.22;168
## 0.9008;13;9;75;-15.22;168
## 0.9009;13;9;75;-15.22;168
## 0.901;13;9;75;-15.22;168
## 0.9011;13;9;75;-15.22;168
## 0.9012;12;8;75;-15.92;166
## 0.9013;12;8;75;-15.92;166
## 0.9014;12;8;75;-15.92;166
## 0.9015;12;8;75;-15.92;166
## 0.9016;12;8;75;-15.92;166
## 0.9017;12;8;75;-15.92;166
## 0.9018;12;8;75;-15.92;165
## 0.9019;12;8;74;-15.8;164
## 0.902;12;8;74;-15.8;164
## 0.9021;12;8;74;-15.8;164
## 0.9022;12;8;74;-15.8;164
## 0.9023;11;7;74;-16.56;162
## 0.9024;11;7;74;-16.56;162
## 0.9025;11;7;74;-16.56;162
## 0.9026;11;7;74;-16.56;161
## 0.9027;11;7;74;-16.56;161
## 0.9028;11;7;73;-16.44;160
## 0.9029;11;7;72;-16.32;159
## 0.903;11;7;72;-16.32;159
## 0.9031;11;7;72;-16.32;159
## 0.9032;11;7;72;-16.32;159
## 0.9033;11;7;72;-16.32;159
## 0.9034;11;7;72;-16.32;159
## 0.9035;11;7;72;-16.32;159
## 0.9036;11;7;72;-16.32;159
## 0.9037;11;7;72;-16.32;158
## 0.9038;11;7;72;-16.32;158
## 0.9039;12;7;72;-15.56;158
## 0.904;12;7;72;-15.56;158
## 0.9041;12;7;72;-15.56;158
## 0.9042;13;7;72;-14.87;158
## 0.9043;13;7;72;-14.87;158
## 0.9044;13;7;72;-14.87;158
## 0.9045;13;7;72;-14.87;158
## 0.9046;13;7;72;-14.87;158
## 0.9047;13;7;72;-14.87;158
## 0.9048;13;7;72;-14.87;158
## 0.9049;13;7;72;-14.87;158
## 0.905;13;7;72;-14.87;158
## 0.9051;13;7;72;-14.87;157
## 0.9052;13;7;72;-14.87;156
## 0.9053;13;7;72;-14.87;156
## 0.9054;13;7;72;-14.87;156
## 0.9055;14;8;72;-14.22;156
## 0.9056;14;8;72;-14.22;156
## 0.9057;14;8;72;-14.22;156
## 0.9058;14;8;72;-14.22;156
## 0.9059;14;8;72;-14.22;156
## 0.906;14;8;72;-14.22;156
## 0.9061;14;8;72;-14.22;156
## 0.9062;14;8;72;-14.22;156
## 0.9063;14;8;72;-14.22;156
## 0.9064;14;8;72;-14.22;156
## 0.9065;14;8;72;-14.22;156
## 0.9066;14;8;72;-14.22;156
## 0.9067;14;8;72;-14.22;156
## 0.9068;14;8;72;-14.22;155
## 0.9069;14;8;72;-14.22;155
## 0.907;14;8;72;-14.22;155
## 0.9071;14;8;72;-14.22;155
## 0.9072;14;8;72;-14.22;155
## 0.9073;14;8;72;-14.22;155
## 0.9074;14;8;72;-14.22;154
## 0.9075;14;8;72;-14.22;154
## 0.9076;14;8;72;-14.22;154
## 0.9077;14;8;72;-14.22;154
## 0.9078;14;8;72;-14.22;154
## 0.9079;14;8;72;-14.22;154
## 0.908;14;8;72;-14.22;154
## 0.9081;14;8;71;-14.1;153
## 0.9082;14;8;71;-14.1;153
## 0.9083;14;8;71;-14.1;153
## 0.9084;14;8;71;-14.1;153
## 0.9085;14;8;71;-14.1;153
## 0.9086;14;8;71;-14.1;153
## 0.9087;14;8;71;-14.1;153
## 0.9088;14;8;71;-14.1;153
## 0.9089;14;8;71;-14.1;153
## 0.909;14;8;71;-14.1;153
## 0.9091;15;8;44;-9.347;153
## 0.9092;15;8;44;-9.347;152
## 0.9093;15;8;44;-9.347;152
## 0.9094;15;8;44;-9.347;151
## 0.9095;15;8;44;-9.347;151
## 0.9096;15;8;44;-9.347;151
## 0.9097;15;8;44;-9.347;151
## 0.9098;16;9;44;-8.787;151
## 0.9099;16;9;44;-8.787;151
## 0.91;16;9;44;-8.787;151
## 0.9101;16;9;44;-8.787;151
## 0.9102;16;9;44;-8.787;150
## 0.9103;16;9;44;-8.787;150
## 0.9104;16;9;44;-8.787;149
## 0.9105;16;9;44;-8.787;149
## 0.9106;16;9;44;-8.787;149
## 0.9107;15;8;44;-9.347;147
## 0.9108;15;8;44;-9.347;147
## 0.9109;15;8;44;-9.347;147
## 0.911;14;7;44;-9.946;145
## 0.9111;14;7;44;-9.946;145
## 0.9112;14;7;44;-9.946;145
## 0.9113;14;7;44;-9.946;145
## 0.9114;14;7;44;-9.946;145
## 0.9115;14;7;44;-9.946;145
## 0.9116;14;7;44;-9.946;145
## 0.9117;14;7;44;-9.946;145
## 0.9118;14;7;44;-9.946;145
## 0.9119;14;7;44;-9.946;145
## 0.912;14;7;44;-9.946;145
## 0.9121;14;7;44;-9.946;145
## 0.9122;15;8;44;-9.347;145
## 0.9123;15;8;44;-9.347;144
## 0.9124;15;8;44;-9.347;144
## 0.9125;15;8;44;-9.347;144
## 0.9126;15;8;44;-9.347;144
## 0.9127;15;8;44;-9.347;144
## 0.9128;15;8;44;-9.347;144
## 0.9129;15;8;44;-9.347;144
## 0.913;15;8;44;-9.347;144
## 0.9131;15;8;44;-9.347;144
## 0.9132;15;8;44;-9.347;144
## 0.9133;15;8;44;-9.347;144
## 0.9134;14;7;44;-9.946;142
## 0.9135;14;7;44;-9.946;142
## 0.9136;14;7;44;-9.946;142
## 0.9137;14;7;43;-9.747;141
## 0.9138;14;7;43;-9.747;141
## 0.9139;14;7;43;-9.747;141
## 0.914;14;7;43;-9.747;141
## 0.9141;14;7;43;-9.747;141
## 0.9142;14;7;43;-9.747;140
## 0.9143;14;7;43;-9.747;140
## 0.9144;14;7;43;-9.747;140
## 0.9145;14;7;43;-9.747;140
## 0.9146;14;7;43;-9.747;140
## 0.9147;14;7;43;-9.747;140
## 0.9148;14;7;43;-9.747;139
## 0.9149;14;7;43;-9.747;139
## 0.915;13;6;43;-10.39;137
## 0.9151;13;6;43;-10.39;137
## 0.9152;13;6;43;-10.39;137
## 0.9153;13;6;43;-10.39;137
## 0.9154;14;6;43;-9.747;137
## 0.9155;14;6;43;-9.747;137
## 0.9156;14;6;43;-9.747;137
## 0.9157;14;6;42;-9.542;136
## 0.9158;14;6;42;-9.542;136
## 0.9159;13;5;42;-10.19;134
## 0.916;13;5;42;-10.19;133
## 0.9161;13;5;42;-10.19;133
## 0.9162;13;5;42;-10.19;133
## 0.9163;13;5;42;-10.19;133
## 0.9164;13;5;42;-10.19;133
## 0.9165;13;5;42;-10.19;133
## 0.9166;13;5;42;-10.19;133
## 0.9167;13;5;42;-10.19;133
## 0.9168;13;5;42;-10.19;133
## 0.9169;13;5;42;-10.19;133
## 0.917;13;5;42;-10.19;133
## 0.9171;13;5;42;-10.19;133
## 0.9172;13;5;42;-10.19;133
## 0.9173;13;5;42;-10.19;133
## 0.9174;13;5;42;-10.19;133
## 0.9175;13;5;42;-10.19;133
## 0.9176;13;5;42;-10.19;132
## 0.9177;13;5;42;-10.19;132
## 0.9178;13;5;42;-10.19;132
## 0.9179;13;5;42;-10.19;132
## 0.918;13;5;42;-10.19;132
## 0.9181;13;5;42;-10.19;132
## 0.9182;13;5;42;-10.19;132
## 0.9183;13;5;42;-10.19;132
## 0.9184;13;5;42;-10.19;132
## 0.9185;13;5;42;-10.19;132
## 0.9186;13;5;42;-10.19;132
## 0.9187;13;5;42;-10.19;132
## 0.9188;13;5;42;-10.19;132
## 0.9189;13;5;42;-10.19;132
## 0.919;13;5;42;-10.19;132
## 0.9191;13;5;42;-10.19;132
## 0.9192;14;6;42;-9.542;132
## 0.9193;14;6;42;-9.542;132
## 0.9194;14;6;42;-9.542;132
## 0.9195;14;6;42;-9.542;132
## 0.9196;14;6;42;-9.542;132
## 0.9197;14;6;42;-9.542;132
## 0.9198;14;6;42;-9.542;132
## 0.9199;14;6;42;-9.542;132
## 0.92;14;6;42;-9.542;132
## 0.9201;14;6;42;-9.542;132
## 0.9202;14;6;42;-9.542;132
## 0.9203;14;6;42;-9.542;132
## 0.9204;14;6;42;-9.542;132
## 0.9205;14;6;42;-9.542;132
## 0.9206;14;6;42;-9.542;132
## 0.9207;14;6;42;-9.542;132
## 0.9208;14;6;42;-9.542;132
## 0.9209;14;6;42;-9.542;132
## 0.921;14;6;42;-9.542;132
## 0.9211;14;6;42;-9.542;132
## 0.9212;14;6;42;-9.542;132
## 0.9213;14;6;41;-9.333;131
## 0.9214;14;6;41;-9.333;131
## 0.9215;14;6;41;-9.333;131
## 0.9216;13;5;41;-9.977;129
## 0.9217;13;5;41;-9.977;129
## 0.9218;13;5;41;-9.977;129
## 0.9219;14;5;38;-8.673;129
## 0.922;14;5;38;-8.673;129
## 0.9221;14;5;38;-8.673;129
## 0.9222;14;5;38;-8.673;128
## 0.9223;14;5;38;-8.673;128
## 0.9224;14;5;38;-8.673;128
## 0.9225;14;5;38;-8.673;128
## 0.9226;14;5;38;-8.673;128
## 0.9227;14;5;38;-8.673;128
## 0.9228;14;5;37;-8.441;127
## 0.9229;14;5;37;-8.441;127
## 0.923;14;5;37;-8.441;127
## 0.9231;14;5;37;-8.441;127
## 0.9232;14;5;37;-8.441;127
## 0.9233;14;5;37;-8.441;127
## 0.9234;14;5;37;-8.441;127
## 0.9235;14;5;37;-8.441;127
## 0.9236;14;5;37;-8.441;127
## 0.9237;14;5;37;-8.441;127
## 0.9238;14;5;37;-8.441;127
## 0.9239;14;5;37;-8.441;127
## 0.924;14;5;37;-8.441;127
## 0.9241;14;5;37;-8.441;127
## 0.9242;14;5;37;-8.441;127
## 0.9243;14;5;37;-8.441;127
## 0.9244;14;5;37;-8.441;127
## 0.9245;14;5;37;-8.441;127
## 0.9246;14;5;37;-8.441;127
## 0.9247;14;5;37;-8.441;127
## 0.9248;14;5;36;-8.203;126
## 0.9249;14;5;36;-8.203;126
## 0.925;14;5;36;-8.203;126
## 0.9251;14;5;36;-8.203;126
## 0.9252;14;5;36;-8.203;125
## 0.9253;14;5;36;-8.203;125
## 0.9254;14;5;36;-8.203;125
## 0.9255;14;5;36;-8.203;125
## 0.9256;14;5;36;-8.203;125
## 0.9257;14;5;36;-8.203;125
## 0.9258;14;5;36;-8.203;125
## 0.9259;14;5;36;-8.203;125
## 0.926;14;5;36;-8.203;125
## 0.9261;14;5;36;-8.203;125
## 0.9262;14;5;36;-8.203;125
## 0.9263;14;5;36;-8.203;125
## 0.9264;14;5;36;-8.203;125
## 0.9265;14;5;36;-8.203;125
## 0.9266;14;5;36;-8.203;125
## 0.9267;14;6;36;-8.203;124
## 0.9268;14;6;36;-8.203;124
## 0.9269;14;6;36;-8.203;124
## 0.927;14;6;36;-8.203;124
## 0.9271;14;6;36;-8.203;124
## 0.9272;14;6;36;-8.203;124
## 0.9273;14;6;36;-8.203;124
## 0.9274;13;5;36;-8.847;122
## 0.9275;13;5;36;-8.847;122
## 0.9276;13;5;36;-8.847;122
## 0.9277;13;5;36;-8.847;122
## 0.9278;13;5;36;-8.847;122
## 0.9279;13;5;36;-8.847;122
## 0.928;13;5;36;-8.847;122
## 0.9281;13;5;36;-8.847;122
## 0.9282;13;5;36;-8.847;122
## 0.9283;13;5;36;-8.847;122
## 0.9284;13;5;36;-8.847;122
## 0.9285;13;5;36;-8.847;122
## 0.9286;13;5;36;-8.847;122
## 0.9287;13;5;36;-8.847;122
## 0.9288;13;5;36;-8.847;122
## 0.9289;13;5;36;-8.847;122
## 0.929;13;5;36;-8.847;122
## 0.9291;13;5;36;-8.847;122
## 0.9292;13;5;36;-8.847;122
## 0.9293;13;5;36;-8.847;122
## 0.9294;13;5;36;-8.847;122
## 0.9295;13;5;36;-8.847;122
## 0.9296;13;5;36;-8.847;122
## 0.9297;13;5;36;-8.847;122
## 0.9298;13;5;36;-8.847;122
## 0.9299;13;5;36;-8.847;122
## 0.93;13;5;36;-8.847;122
## 0.9301;13;5;36;-8.847;122
## 0.9302;13;5;36;-8.847;122
## 0.9303;13;5;36;-8.847;122
## 0.9304;13;5;36;-8.847;122
## 0.9305;13;5;36;-8.847;122
## 0.9306;13;5;36;-8.847;122
## 0.9307;13;5;36;-8.847;122
## 0.9308;12;4;36;-9.542;120
## 0.9309;12;4;36;-9.542;120
## 0.931;12;4;36;-9.542;120
## 0.9311;12;4;36;-9.542;120
## 0.9312;12;4;36;-9.542;120
## 0.9313;12;4;36;-9.542;120
## 0.9314;12;4;36;-9.542;120
## 0.9315;12;4;36;-9.542;120
## 0.9316;12;4;36;-9.542;120
## 0.9317;12;4;36;-9.542;120
## 0.9318;12;4;36;-9.542;120
## 0.9319;12;4;36;-9.542;120
## 0.932;12;4;36;-9.542;120
## 0.9321;12;4;36;-9.542;120
## 0.9322;12;4;36;-9.542;120
## 0.9323;12;4;36;-9.542;120
## 0.9324;12;4;36;-9.542;120
## 0.9325;12;4;36;-9.542;120
## 0.9326;11;3;36;-10.3;118
## 0.9327;11;3;36;-10.3;118
## 0.9328;11;3;36;-10.3;118
## 0.9329;11;3;36;-10.3;118
## 0.933;11;3;36;-10.3;118
## 0.9331;11;3;36;-10.3;118
## 0.9332;11;3;36;-10.3;118
## 0.9333;11;3;36;-10.3;118
## 0.9334;11;3;36;-10.3;118
## 0.9335;11;3;36;-10.3;118
## 0.9336;11;3;36;-10.3;118
## 0.9337;11;3;36;-10.3;118
## 0.9338;11;3;36;-10.3;118
## 0.9339;12;4;36;-9.542;118
## 0.934;12;4;36;-9.542;118
## 0.9341;12;4;36;-9.542;118
## 0.9342;14;4;31;-6.905;118
## 0.9343;14;4;31;-6.905;118
## 0.9344;14;4;31;-6.905;118
## 0.9345;14;4;31;-6.905;118
## 0.9346;14;4;31;-6.905;118
## 0.9347;14;4;31;-6.905;117
## 0.9348;14;4;31;-6.905;117
## 0.9349;14;4;31;-6.905;117
## 0.935;14;4;31;-6.905;117
## 0.9351;14;4;31;-6.905;117
## 0.9352;14;4;31;-6.905;117
## 0.9353;14;4;31;-6.905;117
## 0.9354;14;4;31;-6.905;117
## 0.9355;14;4;31;-6.905;117
## 0.9356;14;4;31;-6.905;117
## 0.9357;14;4;31;-6.905;117
## 0.9358;14;4;31;-6.905;117
## 0.9359;14;4;31;-6.905;117
## 0.936;14;4;31;-6.905;117
## 0.9361;14;4;31;-6.905;117
## 0.9362;14;4;31;-6.905;117
## 0.9363;14;4;31;-6.905;117
## 0.9364;14;4;31;-6.905;117
## 0.9365;14;4;31;-6.905;117
## 0.9366;14;4;31;-6.905;117
## 0.9367;14;4;31;-6.905;117
## 0.9368;15;4;31;-6.305;117
## 0.9369;15;4;31;-6.305;117
## 0.937;15;4;31;-6.305;116
## 0.9371;15;4;31;-6.305;116
## 0.9372;15;4;31;-6.305;116
## 0.9373;15;4;31;-6.305;116
## 0.9374;16;4;31;-5.745;116
## 0.9375;16;4;31;-5.745;115
## 0.9376;16;4;31;-5.745;114
## 0.9377;16;4;31;-5.745;114
## 0.9378;16;4;31;-5.745;114
## 0.9379;16;4;30;-5.46;113
## 0.938;16;4;30;-5.46;113
## 0.9381;16;4;30;-5.46;113
## 0.9382;16;4;30;-5.46;112
## 0.9383;16;4;30;-5.46;112
## 0.9384;16;4;30;-5.46;112
## 0.9385;16;4;30;-5.46;112
## 0.9386;16;4;30;-5.46;112
## 0.9387;16;4;30;-5.46;112
## 0.9388;16;4;30;-5.46;112
## 0.9389;16;4;30;-5.46;112
## 0.939;16;4;30;-5.46;112
## 0.9391;16;4;30;-5.46;112
## 0.9392;16;4;30;-5.46;112
## 0.9393;16;4;30;-5.46;112
## 0.9394;16;4;30;-5.46;112
## 0.9395;16;4;30;-5.46;112
## 0.9396;16;4;30;-5.46;112
## 0.9397;16;4;30;-5.46;112
## 0.9398;16;4;29;-5.166;111
## 0.9399;16;4;29;-5.166;111
## 0.94;16;4;29;-5.166;111
## 0.9401;16;4;29;-5.166;111
## 0.9402;16;4;29;-5.166;111
## 0.9403;16;4;29;-5.166;110
## 0.9404;16;4;29;-5.166;110
## 0.9405;16;4;29;-5.166;110
## 0.9406;16;4;29;-5.166;110
## 0.9407;16;4;29;-5.166;110
## 0.9408;16;4;29;-5.166;110
## 0.9409;16;4;29;-5.166;110
## 0.941;16;4;29;-5.166;109
## 0.9411;16;4;29;-5.166;109
## 0.9412;16;4;29;-5.166;108
## 0.9413;16;4;29;-5.166;108
## 0.9414;16;4;29;-5.166;108
## 0.9415;16;4;29;-5.166;108
## 0.9416;16;4;29;-5.166;108
## 0.9417;16;4;29;-5.166;108
## 0.9418;16;4;29;-5.166;108
## 0.9419;17;5;29;-4.639;108
## 0.942;17;5;29;-4.639;108
## 0.9421;17;5;29;-4.639;108
## 0.9422;17;5;29;-4.639;108
## 0.9423;17;5;29;-4.639;108
## 0.9424;17;5;29;-4.639;108
## 0.9425;17;5;29;-4.639;108
## 0.9426;17;5;29;-4.639;108
## 0.9427;17;5;29;-4.639;108
## 0.9428;17;5;29;-4.639;108
## 0.9429;17;5;29;-4.639;108
## 0.943;17;5;29;-4.639;108
## 0.9431;16;4;29;-5.166;106
## 0.9432;16;4;29;-5.166;106
## 0.9433;16;4;29;-5.166;106
## 0.9434;16;4;29;-5.166;106
## 0.9435;16;4;29;-5.166;106
## 0.9436;16;4;29;-5.166;106
## 0.9437;16;4;29;-5.166;106
## 0.9438;16;4;29;-5.166;106
## 0.9439;16;4;29;-5.166;105
## 0.944;16;4;29;-5.166;105
## 0.9441;16;4;29;-5.166;105
## 0.9442;16;4;29;-5.166;105
## 0.9443;16;4;29;-5.166;104
## 0.9444;16;4;29;-5.166;104
## 0.9445;16;4;29;-5.166;104
## 0.9446;16;4;29;-5.166;104
## 0.9447;16;4;29;-5.166;104
## 0.9448;16;4;29;-5.166;104
## 0.9449;16;4;29;-5.166;104
## 0.945;16;4;29;-5.166;104
## 0.9451;16;4;29;-5.166;104
## 0.9452;16;4;29;-5.166;104
## 0.9453;17;5;29;-4.639;104
## 0.9454;17;5;29;-4.639;104
## 0.9455;17;5;29;-4.639;104
## 0.9456;17;5;29;-4.639;104
## 0.9457;17;5;29;-4.639;104
## 0.9458;17;5;29;-4.639;104
## 0.9459;17;5;29;-4.639;104
## 0.946;17;5;29;-4.639;104
## 0.9461;17;5;29;-4.639;104
## 0.9462;17;5;29;-4.639;104
## 0.9463;18;6;29;-4.143;104
## 0.9464;18;6;29;-4.143;104
## 0.9465;18;6;29;-4.143;104
## 0.9466;18;6;29;-4.143;104
## 0.9467;18;7;29;-4.143;103
## 0.9468;18;7;29;-4.143;103
## 0.9469;18;7;29;-4.143;103
## 0.947;18;7;29;-4.143;103
## 0.9471;18;7;29;-4.143;103
## 0.9472;18;7;29;-4.143;103
## 0.9473;18;7;29;-4.143;103
## 0.9474;18;7;29;-4.143;103
## 0.9475;18;7;29;-4.143;103
## 0.9476;18;7;29;-4.143;103
## 0.9477;18;7;29;-4.143;103
## 0.9478;18;7;29;-4.143;103
## 0.9479;18;7;29;-4.143;103
## 0.948;18;7;29;-4.143;103
## 0.9481;18;7;29;-4.143;103
## 0.9482;18;7;29;-4.143;102
## 0.9483;18;7;29;-4.143;102
## 0.9484;18;7;29;-4.143;102
## 0.9485;18;7;29;-4.143;102
## 0.9486;18;7;29;-4.143;102
## 0.9487;18;7;29;-4.143;102
## 0.9488;18;7;29;-4.143;102
## 0.9489;18;7;29;-4.143;102
## 0.949;18;7;29;-4.143;102
## 0.9491;18;7;29;-4.143;102
## 0.9492;18;7;29;-4.143;101
## 0.9493;18;7;29;-4.143;101
## 0.9494;18;8;29;-4.143;100
## 0.9495;18;8;29;-4.143;100
## 0.9496;18;8;29;-4.143;100
## 0.9497;18;8;29;-4.143;100
## 0.9498;18;8;29;-4.143;99
## 0.9499;18;8;29;-4.143;99
## 0.95;19;8;29;-3.673;99
## 0.9501;19;8;29;-3.673;99
## 0.9502;19;8;29;-3.673;99
## 0.9503;20;9;29;-3.227;99
## 0.9504;20;9;29;-3.227;99
## 0.9505;20;9;29;-3.227;99
## 0.9506;20;9;29;-3.227;99
## 0.9507;20;9;29;-3.227;99
## 0.9508;20;9;29;-3.227;99
## 0.9509;20;9;29;-3.227;99
## 0.951;19;8;28;-3.368;96
## 0.9511;19;8;28;-3.368;96
## 0.9512;19;8;28;-3.368;96
## 0.9513;19;8;28;-3.368;96
## 0.9514;19;8;28;-3.368;96
## 0.9515;19;8;28;-3.368;96
## 0.9516;19;8;28;-3.368;96
## 0.9517;19;8;28;-3.368;96
## 0.9518;19;9;28;-3.368;95
## 0.9519;19;9;28;-3.368;95
## 0.952;19;9;28;-3.368;95
## 0.9521;19;9;28;-3.368;95
## 0.9522;19;9;28;-3.368;95
## 0.9523;18;8;28;-3.838;93
## 0.9524;18;8;28;-3.838;93
## 0.9525;18;8;28;-3.838;93
## 0.9526;18;8;28;-3.838;92
## 0.9527;18;8;28;-3.838;92
## 0.9528;18;8;28;-3.838;92
## 0.9529;18;8;28;-3.838;92
## 0.953;18;8;28;-3.838;92
## 0.9531;18;8;28;-3.838;92
## 0.9532;18;8;28;-3.838;92
## 0.9533;18;8;28;-3.838;92
## 0.9534;18;8;28;-3.838;92
## 0.9535;18;8;28;-3.838;92
## 0.9536;18;8;28;-3.838;92
## 0.9537;18;8;28;-3.838;92
## 0.9538;18;8;28;-3.838;92
## 0.9539;18;8;28;-3.838;92
## 0.954;18;8;28;-3.838;92
## 0.9541;18;8;28;-3.838;92
## 0.9542;18;8;28;-3.838;92
## 0.9543;18;8;28;-3.838;92
## 0.9544;18;8;28;-3.838;92
## 0.9545;18;8;28;-3.838;92
## 0.9546;18;8;28;-3.838;92
## 0.9547;18;8;28;-3.838;92
## 0.9548;17;7;28;-4.334;90
## 0.9549;17;7;28;-4.334;90
## 0.955;17;7;28;-4.334;90
## 0.9551;17;7;28;-4.334;90
## 0.9552;17;7;28;-4.334;90
## 0.9553;17;7;28;-4.334;90
## 0.9554;17;7;28;-4.334;90
## 0.9555;17;7;28;-4.334;90
## 0.9556;17;7;28;-4.334;90
## 0.9557;17;7;28;-4.334;90
## 0.9558;17;8;28;-4.334;89
## 0.9559;17;8;28;-4.334;89
## 0.956;17;8;28;-4.334;89
## 0.9561;17;8;28;-4.334;89
## 0.9562;17;8;28;-4.334;89
## 0.9563;17;8;28;-4.334;89
## 0.9564;17;8;28;-4.334;89
## 0.9565;17;8;28;-4.334;89
## 0.9566;17;9;28;-4.334;88
## 0.9567;17;9;28;-4.334;87
## 0.9568;17;9;28;-4.334;87
## 0.9569;17;9;28;-4.334;87
## 0.957;17;9;28;-4.334;87
## 0.9571;17;9;28;-4.334;87
## 0.9572;17;9;28;-4.334;87
## 0.9573;17;9;28;-4.334;87
## 0.9574;17;9;28;-4.334;86
## 0.9575;17;9;28;-4.334;86
## 0.9576;17;9;28;-4.334;86
## 0.9577;17;9;28;-4.334;86
## 0.9578;17;9;28;-4.334;86
## 0.9579;17;9;28;-4.334;86
## 0.958;17;9;28;-4.334;86
## 0.9581;17;9;28;-4.334;86
## 0.9582;17;9;28;-4.334;86
## 0.9583;17;9;28;-4.334;86
## 0.9584;17;9;28;-4.334;86
## 0.9585;17;9;28;-4.334;86
## 0.9586;17;9;28;-4.334;86
## 0.9587;17;9;28;-4.334;86
## 0.9588;17;9;28;-4.334;86
## 0.9589;17;9;28;-4.334;86
## 0.959;16;8;28;-4.861;84
## 0.9591;16;8;28;-4.861;84
## 0.9592;16;8;28;-4.861;84
## 0.9593;16;8;27;-4.545;83
## 0.9594;16;8;27;-4.545;83
## 0.9595;16;8;27;-4.545;83
## 0.9596;16;8;27;-4.545;83
## 0.9597;16;8;27;-4.545;83
## 0.9598;16;8;26;-4.217;82
## 0.9599;16;8;26;-4.217;82
## 0.96;16;8;26;-4.217;82
## 0.9601;16;8;26;-4.217;82
## 0.9602;16;8;26;-4.217;82
## 0.9603;17;8;21;-1.835;82
## 0.9604;17;8;21;-1.835;82
## 0.9605;17;8;21;-1.835;82
## 0.9606;17;8;21;-1.835;82
## 0.9607;17;8;21;-1.835;82
## 0.9608;17;8;21;-1.835;82
## 0.9609;16;7;21;-2.362;80
## 0.961;17;8;20;-1.412;79
## 0.9611;18;8;11;4.278;78
## 0.9612;18;8;11;4.278;78
## 0.9613;18;8;11;4.278;78
## 0.9614;18;8;11;4.278;78
## 0.9615;18;8;11;4.278;78
## 0.9616;18;8;11;4.278;78
## 0.9617;18;8;11;4.278;77
## 0.9618;18;8;11;4.278;77
## 0.9619;18;8;11;4.278;77
## 0.962;18;8;11;4.278;76
## 0.9621;18;8;11;4.278;76
## 0.9622;18;8;11;4.278;76
## 0.9623;18;8;11;4.278;76
## 0.9624;18;8;11;4.278;76
## 0.9625;18;8;11;4.278;76
## 0.9626;18;8;11;4.278;76
## 0.9627;18;8;11;4.278;76
## 0.9628;17;7;11;3.781;74
## 0.9629;17;7;11;3.781;74
## 0.963;17;7;11;3.781;74
## 0.9631;17;7;11;3.781;74
## 0.9632;17;7;11;3.781;74
## 0.9633;17;7;11;3.781;74
## 0.9634;17;7;11;3.781;74
## 0.9635;17;7;11;3.781;74
## 0.9636;17;7;11;3.781;74
## 0.9637;17;7;11;3.781;74
## 0.9638;17;7;11;3.781;74
## 0.9639;17;7;11;3.781;74
## 0.964;17;7;11;3.781;74
## 0.9641;17;7;11;3.781;74
## 0.9642;17;7;11;3.781;74
## 0.9643;17;7;11;3.781;74
## 0.9644;17;7;11;3.781;74
## 0.9645;17;7;11;3.781;74
## 0.9646;17;7;11;3.781;72
## 0.9647;16;6;11;3.255;70
## 0.9648;16;6;11;3.255;70
## 0.9649;16;6;11;3.255;70
## 0.965;16;6;11;3.255;70
## 0.9651;16;6;11;3.255;70
## 0.9652;16;6;11;3.255;70
## 0.9653;16;6;11;3.255;70
## 0.9654;16;6;11;3.255;70
## 0.9655;16;6;11;3.255;70
## 0.9656;16;6;11;3.255;70
## 0.9657;16;6;11;3.255;70
## 0.9658;16;6;11;3.255;70
## 0.9659;16;6;11;3.255;70
## 0.966;16;6;11;3.255;70
## 0.9661;16;6;11;3.255;70
## 0.9662;16;6;11;3.255;70
## 0.9663;16;6;11;3.255;70
## 0.9664;16;6;11;3.255;69
## 0.9665;16;6;11;3.255;69
## 0.9666;15;5;11;2.694;67
## 0.9667;15;5;11;2.694;67
## 0.9668;15;5;11;2.694;67
## 0.9669;15;6;11;2.694;66
## 0.967;15;6;11;2.694;66
## 0.9671;15;6;11;2.694;66
## 0.9672;14;5;11;2.095;64
## 0.9673;14;5;11;2.095;64
## 0.9674;14;5;11;2.095;64
## 0.9675;14;5;11;2.095;64
## 0.9676;14;5;11;2.095;64
## 0.9677;14;5;11;2.095;64
## 0.9678;14;5;11;2.095;64
## 0.9679;14;5;11;2.095;63
## 0.968;14;5;11;2.095;63
## 0.9681;14;5;11;2.095;63
## 0.9682;14;5;11;2.095;63
## 0.9683;14;5;11;2.095;63
## 0.9684;14;5;11;2.095;62
## 0.9685;14;5;11;2.095;62
## 0.9686;14;5;11;2.095;62
## 0.9687;14;5;11;2.095;62
## 0.9688;14;5;11;2.095;62
## 0.9689;13;4;11;1.451;60
## 0.969;13;4;11;1.451;60
## 0.9691;13;4;11;1.451;60
## 0.9692;13;4;11;1.451;60
## 0.9693;13;4;11;1.451;60
## 0.9694;13;4;11;1.451;60
## 0.9695;13;4;11;1.451;60
## 0.9696;13;4;11;1.451;60
## 0.9697;13;4;11;1.451;59
## 0.9698;13;4;11;1.451;59
## 0.9699;13;4;11;1.451;59
## 0.97;13;4;11;1.451;59
## 0.9701;13;4;11;1.451;59
## 0.9702;13;4;11;1.451;58
## 0.9703;13;4;11;1.451;58
## 0.9704;13;4;11;1.451;58
## 0.9705;13;4;11;1.451;58
## 0.9706;13;4;11;1.451;58
## 0.9707;13;4;11;1.451;58
## 0.9708;13;4;11;1.451;58
## 0.9709;13;4;11;1.451;58
## 0.971;13;4;11;1.451;58
## 0.9711;13;4;11;1.451;58
## 0.9712;13;4;11;1.451;58
## 0.9713;13;4;11;1.451;56
## 0.9714;13;4;10;2.279;55
## 0.9715;13;4;10;2.279;55
## 0.9716;13;4;10;2.279;55
## 0.9717;14;5;8;4.861;55
## 0.9718;14;5;8;4.861;55
## 0.9719;14;5;8;4.861;55
## 0.972;14;5;8;4.861;55
## 0.9721;14;5;8;4.861;55
## 0.9722;14;5;8;4.861;55
## 0.9723;14;5;8;4.861;55
## 0.9724;14;5;8;4.861;55
## 0.9725;14;5;8;4.861;55
## 0.9726;14;5;8;4.861;55
## 0.9727;14;6;8;4.861;54
## 0.9728;14;6;8;4.861;54
## 0.9729;13;5;8;4.217;52
## 0.973;13;5;8;4.217;52
## 0.9731;13;5;8;4.217;51
## 0.9732;13;5;8;4.217;51
## 0.9733;12;4;8;3.522;49
## 0.9734;11;3;8;2.766;47
## 0.9735;11;3;8;2.766;47
## 0.9736;11;3;8;2.766;47
## 0.9737;11;3;8;2.766;47
## 0.9738;11;3;8;2.766;47
## 0.9739;11;3;8;2.766;47
## 0.974;11;3;8;2.766;47
## 0.9741;11;3;8;2.766;47
## 0.9742;11;3;8;2.766;47
## 0.9743;10;2;8;1.938;45
## 0.9744;10;2;8;1.938;45
## 0.9745;10;2;8;1.938;44
## 0.9746;10;2;8;1.938;44
## 0.9747;10;2;8;1.938;44
## 0.9748;10;2;8;1.938;44
## 0.9749;10;2;8;1.938;44
## 0.975;10;2;8;1.938;44
## 0.9751;10;2;8;1.938;44
## 0.9752;10;2;8;1.938;44
## 0.9753;10;2;8;1.938;44
## 0.9754;10;2;8;1.938;44
## 0.9755;10;2;8;1.938;44
## 0.9756;10;2;7;3.098;43
## 0.9757;10;2;7;3.098;43
## 0.9758;10;2;7;3.098;43
## 0.9759;10;2;7;3.098;43
## 0.976;10;2;7;3.098;43
## 0.9761;10;2;7;3.098;43
## 0.9762;10;2;7;3.098;43
## 0.9763;10;2;7;3.098;43
## 0.9764;10;2;7;3.098;43
## 0.9765;10;2;7;3.098;43
## 0.9766;10;2;7;3.098;43
## 0.9767;10;2;7;3.098;43
## 0.9768;10;2;7;3.098;43
## 0.9769;10;2;7;3.098;43
## 0.977;10;2;7;3.098;43
## 0.9771;11;3;7;3.926;43
## 0.9772;11;3;7;3.926;43
## 0.9773;11;3;7;3.926;43
## 0.9774;11;3;7;3.926;43
## 0.9775;11;3;7;3.926;43
## 0.9776;11;3;7;3.926;43
## 0.9777;11;4;7;3.926;42
## 0.9778;11;4;7;3.926;42
## 0.9779;11;4;7;3.926;42
## 0.978;11;4;7;3.926;42
## 0.9781;11;4;7;3.926;42
## 0.9782;11;4;7;3.926;42
## 0.9783;11;4;7;3.926;42
## 0.9784;11;4;7;3.926;42
## 0.9785;11;4;7;3.926;41
## 0.9786;11;4;7;3.926;41
## 0.9787;11;4;7;3.926;41
## 0.9788;11;4;7;3.926;41
## 0.9789;11;4;7;3.926;41
## 0.979;11;4;7;3.926;41
## 0.9791;11;4;7;3.926;41
## 0.9792;11;4;7;3.926;41
## 0.9793;11;4;7;3.926;41
## 0.9794;11;4;7;3.926;41
## 0.9795;10;3;7;3.098;39
## 0.9796;10;3;7;3.098;39
## 0.9797;10;3;7;3.098;39
## 0.9798;10;3;7;3.098;39
## 0.9799;10;3;7;3.098;39
## 0.98;10;3;7;3.098;39
## 0.9801;10;3;7;3.098;39
## 0.9802;10;3;7;3.098;39
## 0.9803;10;3;7;3.098;39
## 0.9804;11;4;6;5.265;39
## 0.9805;11;4;6;5.265;39
## 0.9806;10;3;6;4.437;37
## 0.9807;10;3;6;4.437;37
## 0.9808;10;3;6;4.437;37
## 0.9809;10;3;6;4.437;37
## 0.981;10;4;6;4.437;36
## 0.9811;10;4;6;4.437;36
## 0.9812;9;3;6;3.522;34
## 0.9813;9;3;6;3.522;34
## 0.9814;9;3;6;3.522;34
## 0.9815;9;3;6;3.522;34
## 0.9816;9;3;6;3.522;34
## 0.9817;9;3;6;3.522;34
## 0.9818;9;3;6;3.522;34
## 0.9819;9;3;6;3.522;34
## 0.982;9;3;6;3.522;34
## 0.9821;9;3;5;5.105;33
## 0.9822;9;3;5;5.105;33
## 0.9823;9;3;5;5.105;33
## 0.9824;9;3;5;5.105;33
## 0.9825;9;3;5;5.105;33
## 0.9826;9;3;5;5.105;33
## 0.9827;9;3;5;5.105;32
## 0.9828;9;3;5;5.105;32
## 0.9829;9;3;5;5.105;31
## 0.983;9;3;5;5.105;30
## 0.9831;9;3;5;5.105;30
## 0.9832;9;3;5;5.105;29
## 0.9833;9;3;5;5.105;29
## 0.9834;9;3;4;7.044;28
## 0.9835;9;3;4;7.044;28
## 0.9836;9;3;4;7.044;28
## 0.9837;9;4;4;7.044;27
## 0.9838;9;4;4;7.044;27
## 0.9839;9;4;4;7.044;27
## 0.984;9;4;4;7.044;27
## 0.9841;9;4;4;7.044;27
## 0.9842;9;4;4;7.044;27
## 0.9843;9;4;4;7.044;27
## 0.9844;9;4;4;7.044;27
## 0.9845;9;4;4;7.044;27
## 0.9846;9;4;4;7.044;27
## 0.9847;9;4;4;7.044;27
## 0.9848;9;4;4;7.044;27
## 0.9849;9;4;4;7.044;27
## 0.985;9;4;4;7.044;27
## 0.9851;9;4;4;7.044;27
## 0.9852;9;4;4;7.044;27
## 0.9853;9;4;4;7.044;27
## 0.9854;9;4;4;7.044;27
## 0.9855;9;4;4;7.044;27
## 0.9856;9;4;4;7.044;27
## 0.9857;9;4;4;7.044;27
## 0.9858;9;4;4;7.044;27
## 0.9859;9;4;4;7.044;26
## 0.986;9;4;4;7.044;26
## 0.9861;9;5;4;7.044;25
## 0.9862;9;5;4;7.044;25
## 0.9863;9;5;4;7.044;25
## 0.9864;9;5;4;7.044;25
## 0.9865;9;5;4;7.044;25
## 0.9866;9;5;4;7.044;24
## 0.9867;9;5;4;7.044;24
## 0.9868;9;5;4;7.044;23
## 0.9869;9;5;4;7.044;23
## 0.987;9;5;4;7.044;23
## 0.9871;9;5;4;7.044;23
## 0.9872;9;5;4;7.044;23
## 0.9873;9;5;4;7.044;23
## 0.9874;9;5;4;7.044;23
## 0.9875;9;5;4;7.044;23
## 0.9876;9;5;4;7.044;23
## 0.9877;9;5;4;7.044;23
## 0.9878;9;5;4;7.044;23
## 0.9879;8;4;4;6.021;21
## 0.988;8;4;4;6.021;21
## 0.9881;8;4;4;6.021;21
## 0.9882;8;4;4;6.021;21
## 0.9883;8;4;4;6.021;21
## 0.9884;8;4;4;6.021;21
## 0.9885;8;4;4;6.021;21
## 0.9886;8;4;4;6.021;21
## 0.9887;8;4;4;6.021;21
## 0.9888;8;4;4;6.021;21
## 0.9889;8;4;4;6.021;21
## 0.989;8;4;4;6.021;21
## 0.9891;8;4;4;6.021;21
## 0.9892;8;4;4;6.021;21
## 0.9893;8;4;4;6.021;21
## 0.9894;8;4;4;6.021;21
## 0.9895;8;4;4;6.021;21
## 0.9896;8;4;4;6.021;21
## 0.9897;8;5;4;6.021;20
## 0.9898;8;5;4;6.021;20
## 0.9899;8;5;4;6.021;20
## 0.99;8;5;4;6.021;20
## 0.9901;8;5;4;6.021;20
## 0.9902;8;5;4;6.021;20
## 0.9903;8;6;4;6.021;19
## 0.9904;8;6;4;6.021;19
## 0.9905;8;6;4;6.021;19
## 0.9906;8;6;4;6.021;19
## 0.9907;8;6;4;6.021;19
## 0.9908;8;6;4;6.021;19
## 0.9909;8;7;4;6.021;18
## 0.991;8;7;4;6.021;18
## 0.9911;8;7;4;6.021;18
## 0.9912;8;7;4;6.021;18
## 0.9913;8;7;4;6.021;18
## 0.9914;8;7;4;6.021;18
## 0.9915;8;7;4;6.021;18
## 0.9916;8;7;4;6.021;18
## 0.9917;8;7;4;6.021;18
## 0.9918;8;7;4;6.021;18
## 0.9919;8;7;4;6.021;18
## 0.992;8;7;4;6.021;18
## 0.9921;8;7;4;6.021;18
## 0.9922;8;7;4;6.021;18
## 0.9923;8;7;3;8.519;17
## 0.9924;8;7;3;8.519;17
## 0.9925;8;7;3;8.519;17
## 0.9926;8;7;3;8.519;17
## 0.9927;8;7;3;8.519;17
## 0.9928;8;7;3;8.519;17
## 0.9929;8;7;3;8.519;17
## 0.993;8;7;3;8.519;17
## 0.9931;8;7;3;8.519;17
## 0.9932;8;7;3;8.519;17
## 0.9933;8;7;3;8.519;17
## 0.9934;8;7;3;8.519;17
## 0.9935;8;7;3;8.519;17
## 0.9936;6;5;3;6.021;13
## 0.9937;6;5;3;6.021;13
## 0.9938;6;5;3;6.021;13
## 0.9939;5;5;2;7.959;10
## 0.994;5;5;2;7.959;10
## 0.9941;5;5;2;7.959;10
## 0.9942;5;5;2;7.959;10
## 0.9943;5;5;2;7.959;10
## 0.9944;5;5;2;7.959;10
## 0.9945;5;5;2;7.959;10
## 0.9946;4;4;2;6.021;8
## 0.9947;4;4;2;6.021;8
## 0.9948;4;4;2;6.021;8
## 0.9949;4;4;2;6.021;8
## 0.995;4;4;2;6.021;8
## 0.9951;4;4;2;6.021;8
## 0.9952;4;4;2;6.021;8
## 0.9953;4;4;2;6.021;8
## 0.9954;4;4;2;6.021;8
## 0.9955;4;4;2;6.021;8
## 0.9956;4;4;2;6.021;8
## 0.9957;3;3;2;3.522;6
## 0.9958;3;3;2;3.522;6
## 0.9959;3;3;2;3.522;6
## 0.996;3;3;2;3.522;6
## 0.9961;3;3;2;3.522;6
## 0.9962;3;3;2;3.522;6
## 0.9963;3;3;2;3.522;6
## 0.9964;3;3;2;3.522;6
## 0.9965;3;3;2;3.522;6
## 0.9966;3;3;2;3.522;6
## 0.9967;3;3;2;3.522;6
## 0.9968;3;3;2;3.522;6
## 0.9969;3;3;2;3.522;6
## 0.997;3;3;2;3.522;6
## 0.9971;3;3;2;3.522;6
## 0.9972;3;3;2;3.522;6
## 0.9973;3;3;2;3.522;6
## 0.9974;3;3;2;3.522;6
## 0.9975;3;3;2;3.522;6
## 0.9976;3;3;2;3.522;6
## 0.9977;3;3;2;3.522;6
## 0.9978;3;3;2;3.522;6
## 0.9979;3;3;2;3.522;6
## 0.998;3;3;2;3.522;6
## 0.9981;3;3;2;3.522;6
## 0.9982;3;3;2;3.522;6
## 0.9983;3;3;2;3.522;6
## 0.9984;3;3;2;3.522;6
## 0.9985;3;3;2;3.522;6
## 0.9986;3;3;2;3.522;6
## 0.9987;2;2;2;0;4
## 0.9988;2;2;2;0;4
## 0.9989;2;2;2;0;4
## 0.999;2;2;2;0;4
## 0.9991;2;2;2;0;4
## 0.9992;2;2;2;0;4
## 0.9993;2;2;2;0;4
## 0.9994;2;2;2;0;4
## 0.9995;2;2;2;0;4
## 0.9996;2;2;2;0;4
## 0.9997;2;2;2;0;4
## 0.9998;2;2;2;0;4
## 0.9999;2;2;2;0;4
## # -----------------
## # Optimum Corr Min=0.9, Max=0.9923, Mean=0.9
## Getting the clusters of the peaks...
## Matching clusters with reference metabolites...
## Done.

3. Example of results that can be obtained for each cluster formed:

  • Peaks of the cluster:
nmr_ID_res$Cluster1$cluster.peaks[,1]
##  [1] 0.10 0.30 0.59 0.75 1.20 1.35 1.51 2.31 2.34 2.40 2.64 2.67 2.70 2.88 3.00 3.03 3.06 3.09 3.12 3.15 3.18 3.21 3.24 3.27 3.29 3.33 3.36 3.39 3.42
## [30] 3.45 3.60 4.02 4.05 4.08 4.13 4.17 4.20 4.25 4.28 4.31 4.34 4.38 4.41 4.45 4.50 4.53 4.55 4.58 4.63 4.66 4.71 4.74 5.45 6.20 6.56 6.62 6.70 6.75
## [59] 6.81 6.88 6.92 6.96 7.03 7.09 7.56 7.71 7.74 7.81 7.84 7.87 7.91 7.96 8.14 9.29 9.51 9.66
  • Summary of the top metabolites identified for that cluster, with the respective scores:
knitr::kable(nmr_ID_res$Cluster1$summary, col.names="Jaccard Index")
Jaccard Index
HMDB0001197 0.1798
HMDB0001248 0.1798
HMDB0000033 0.1758
HMDB0001849 0.1650
HMDB0001885 0.1625
HMDB0001434 0.1605
HMDB0001932 0.1489
HMDB0000472 0.1446
HMDB0000866 0.1446
HMDB0000217 0.1429

4. Further results for each metabolite matched:

  • Score (jaccard index)
nmr_ID_res$Cluster1$metabolites.matched[[1]]$score
## [1] 0.1798
  • Peaks of the reference spectrum that matched the cluster
nmr_ID_res$Cluster1$metabolites.matched[[1]]$matched_peaks_ref
##  [1] 2.32 2.39 4.02 4.04 4.05 4.28 4.29 4.31 4.34 4.37 4.40 4.45 4.47 4.50 7.54 7.85
  • Peaks of the cluster that matched the reference spectrum
nmr_ID_res$Cluster1$metabolites.matched[[1]]$matched_peaks_clust
##  [1] 2.31 2.40 4.02 4.05 4.08 4.25 4.28 4.31 4.34 4.38 4.41 4.45 4.50 4.53 7.56 7.84
  • Peaks of the reference spectra
nmr_ID_res$Cluster1$metabolites.matched[[1]]$reference_peaks
##  [1] 2.32 2.39 3.88 3.89 3.90 3.90 4.02 4.04 4.05 4.06 4.06 4.08 4.09 4.28 4.29 4.31 4.34 4.37 4.40 4.45 4.46 4.47 4.49 4.50 4.51 5.82 5.83 7.54 7.60
## [30] 7.85 8.30

16 Pathway Analysis

16.1 Functions to use

1. Get only the paths of the mentioned organism that contain one or more of the given compounds

get_paths_with_cpds_org(organism_code, compounds, full.result=T)

  • organism_code: Organism code. The correct code for an organism can be consulted using function get_OrganismsCodes, mentioned in the additional functions below;

  • compounds: Named vector with kegg codes of compounds and respective names. This vector can be obtained by using the function get_cpd_names or the function convert_hmdb_to_kegg, mentioned in the additional functions below;

  • full.result: boolean value (TRUE or FALSE) indicating if a full result is to be given. Defaults to TRUE.

If full result is chosen, the data frame returned contains information on the pathways of the organism that contains one or more of the given compounds and, for each pathway, the kegg codes (and their names) of the compounds given that are present in that path.

If full result is not wanted, only the pathways will be given.

2. Create the metabolic pathway wanted.

The pathway created contains the compounds, reactions and other paths that it connects to as nodes.

The compounds given in compounds are colored in blue, while the rest of the compounds are colored in grey.

The other paths that it may connect to are colored in orange.

Reversive reactions are colored in green and the irreversible ones in red.

pathway_analysis(compounds, pathway, nodeNames=“kegg”, nodeTooltip=F, map.zoom=F, map.layout=“preset”, map.width=NULL, map.height=NULL)

  • compounds: vector of compounds of interest;

  • pathway: KEGG code (e.g., “hsa00010”) of the path wanted;

  • nodeNames: how the nodes should be named. If “kegg”, nodes are named with kegg codes. If “names”, nodes are named with the common names.

  • nodeTooltip: if a tooltip should appear when hovering a node. Only works in certain environments;

  • map.zoom: if the map should have the zoom in and out option. Only works in certain environments;

  • map.layout: layout of the map, available values are the ones of cytoscape (“breadthfirst”, “preset”, “cose”, …);

  • map.width: width of the map, in percentage;

  • map.height: Height of the map, in px (e.g. “500px”).

16.2 Additional functions

The following function returns a dataframe with the t-number, organism kegg code, full name and phylogeny for each kegg organism:

get_OrganismsCodes()

The following function returns a named vector with the names of the compounds. The names of the vector are the compounds’ names and the vector elements the kegg codes.

get_cpds_names(kegg_codes)

  • kegg_codes: Character vector with kegg codes.

The following function return a named vector with kegg codes and respective metabolite names. Vector names are the compound names and the vector elements the kegg codes:

convert_hmdb_to_kegg(hmdb_codes)

  • hmdb_codes:Vector with the HMDB codes (each hmdb code must have 7 digits, e.g., HMDB0000001).

The following function returns the metabolic kegg paths present in mentioned organism:

get_MetabPaths_org(org_code)

  • org_code: Organism code. The correct code for an organism can be consulted using function get_OrganismsCodes, mentioned above.

16.3 Example

This example uses the results obtained in the example for identification of metabolites from NMR peaks (here).

1. Get identified metabolites in one vector:

id_metabs=c()
for (cluster in nmr_ID_res){
  id_metabs=c(id_metabs, names(cluster$summary))
}
id_metabs
##  [1] "HMDB0001197" "HMDB0001248" "HMDB0000033" "HMDB0001849" "HMDB0001885" "HMDB0001434" "HMDB0001932" "HMDB0000472" "HMDB0000866" "HMDB0000217"
## [11] "HMDB0000921" "HMDB0000077" "HMDB0000032" "HMDB0000501" "HMDB0001830" "HMDB0000937" "HMDB0000761" "HMDB0000871" "HMDB0000374" "HMDB0000899"
## [21] "HMDB0000217" "HMDB0000235" "HMDB0001878" "HMDB0001569" "HMDB0000920" "HMDB0000243" "HMDB0000239" "HMDB0000225" "HMDB0000355" "HMDB0000426"
## [31] "HMDB0000720" "HMDB0000474" "HMDB0000635" "HMDB0000661" "HMDB0001545" "HMDB0003282" "HMDB0002024" "HMDB0000076" "HMDB0000434" "HMDB0000440"
## [41] "HMDB0001930" "HMDB0000462" "HMDB0001874" "HMDB0001238" "HMDB0001389" "HMDB0000115" "HMDB0000639" "HMDB0001511" "HMDB0000863" "HMDB0001847"
## [51] "HMDB0000574" "HMDB0000168" "HMDB0000187" "HMDB0000707" "HMDB0001316" "HMDB0000017" "HMDB0000034" "HMDB0000300" "HMDB0000444" "HMDB0001847"
## [61] "HMDB0000484" "HMDB0001046" "HMDB0000738" "HMDB0000197" "HMDB0000840" "HMDB0001860" "HMDB0000290"

2. Convert metabolites from HMDB codes to KEGG codes:

id_metabs_kegg=convert_hmdb_to_kegg(id_metabs)
## The following hmdb code is not available at kegg:  HMDB0001885 
## The following hmdb code is not available at kegg:  HMDB0001434 
## The following hmdb code is not available at kegg:  HMDB0000501 
## The following hmdb code is not available at kegg:  HMDB0000720 
## The following hmdb code is not available at kegg:  HMDB0000635 
## The following hmdb code is not available at kegg:  HMDB0000434 
## The following hmdb code is not available at kegg:  HMDB0000444 
## The following hmdb code is not available at kegg:  HMDB0001046
#id_metabs_kegg
knitr::kable(cbind(names(id_metabs_kegg), id_metabs_kegg), row.names=F, col.names=c("Names", "KEGG codes"))
Names KEGG codes
FADH cpd:C01352
FAD cpd:C00016
Carnosine cpd:C00386
Propranolol cpd:C07407
Metoprolol cpd:C07202
5-Hydroxy-L-tryptophan cpd:C01017
N-Acetyl-L-tyrosine cpd:C01657
NADP cpd:C00006
Cholestenone cpd:C00599
Dehydroepiandrosterone cpd:C01227
7-Dehydrocholesterol cpd:C01164
Progesterone cpd:C00410
Stigmasterol cpd:C05442
Lithocholic acid cpd:C03990
5alpha-Cholestanone cpd:C03238
17-Hydroxyprogesterone cpd:C01176
Androstanedione cpd:C00674
NADP cpd:C00006
Thiamine cpd:C00378
Thymol cpd:C09908
Epi-coprostanol cpd:C12978
11a-Hydroxyprogesterone cpd:C03747
Pyruvic acid cpd:C00022
Pyridoxine cpd:C00314
Oxoadipic acid cpd:C00322
3-Hydroxymethylglutaric acid cpd:C03761
Citramalic acid cpd:C00815
Butanone cpd:C02845
Glutaric acid cpd:C00489
Pyridoxal cpd:C00250
1-Methylguanine cpd:C04152
Imidazoleacetic acid cpd:C02835
Dihydrouracil cpd:C00429
3-Hydroxyphenylacetic acid cpd:C05593
Ranitidine cpd:D00673
Allantoin cpd:C01551
D-threo-Isocitric acid cpd:C00451
N-Acetylserotonin cpd:C00978
Melatonin cpd:C01598
Glycolic acid cpd:C00160
Galactaric acid cpd:C00879
Phosphocreatine cpd:C02305
Isopropyl alcohol cpd:C01845
Caffeine cpd:C07481
L-Cysteine cpd:C00097
L-Asparagine cpd:C00152
L-Serine cpd:C00065
4-Hydroxyphenylpyruvic acid cpd:C01179
6-Phosphogluconic acid cpd:C00345
4-Pyridoxic acid cpd:C00847
Adenine cpd:C00147
Uracil cpd:C00106
Caffeine cpd:C07481
Vanillic acid cpd:C06672
Indole cpd:C00463
Indoleacetic acid cpd:C00954
Salicyluric acid cpd:C07588
Paraxanthine cpd:C13747
Uridine diphosphate-N-acetylglucosamine cpd:C00043

3. Get the metabolic paths that have one ore more of the identified compounds, obtaining the full results

cpds_paths=specmine::get_paths_with_cpds_org("ath", id_metabs_kegg)
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
knitr::kable(cpds_paths)
pathways compounds compounds_names
Glycolysis / Gluconeogenesis ath00010 cpd:C00022 Pyruvic acid
Citrate cycle (TCA cycle) ath00020 cpd:C00022 Pyruvic acid
Pentose phosphate pathway ath00030 cpd:C00345 6-Phosphogluconic acid
Steroid biosynthesis ath00100 cpd:C01164; cpd:C05442 7-Dehydrocholesterol; Stigmasterol
Ubiquinone and other terpenoid-quinone biosynthesis ath00130 cpd:C01179 4-Hydroxyphenylpyruvic acid
Purine metabolism ath00230 cpd:C00147 Adenine
Caffeine metabolism ath00232 cpd:C13747 Paraxanthine
Pyrimidine metabolism ath00240 cpd:C00429; cpd:C00106 Dihydrouracil; Uracil
Alanine, aspartate and glutamate metabolism ath00250 cpd:C00152; cpd:C00022 L-Asparagine; Pyruvic acid
Glycine, serine and threonine metabolism ath00260 cpd:C00065; cpd:C00022 L-Serine; Pyruvic acid
Monobactam biosynthesis ath00261 cpd:C00022 Pyruvic acid
Cysteine and methionine metabolism ath00270 cpd:C00065; cpd:C00097; cpd:C00022 L-Serine; L-Cysteine; Pyruvic acid
Valine, leucine and isoleucine biosynthesis ath00290 cpd:C00022 Pyruvic acid
Lysine degradation ath00310 cpd:C00322 Oxoadipic acid
Histidine metabolism ath00340 cpd:C02835 Imidazoleacetic acid
Tyrosine metabolism ath00350 cpd:C01179; cpd:C00022 4-Hydroxyphenylpyruvic acid; Pyruvic acid
Tryptophan metabolism ath00380 cpd:C00978; cpd:C00322; cpd:C01598; cpd:C00954 N-Acetylserotonin; Oxoadipic acid; Melatonin; Indoleacetic acid
Phenylalanine, tyrosine and tryptophan biosynthesis ath00400 cpd:C00463; cpd:C01179 Indole; 4-Hydroxyphenylpyruvic acid
beta-Alanine metabolism ath00410 cpd:C00429; cpd:C00106 Dihydrouracil; Uracil
Cyanoamino acid metabolism ath00460 cpd:C00097; cpd:C00152; cpd:C00065 L-Cysteine; L-Asparagine; L-Serine
Glutathione metabolism ath00480 cpd:C00006; cpd:C00097 NADP; L-Cysteine
Amino sugar and nucleotide sugar metabolism ath00520 cpd:C00043 Uridine diphosphate-N-acetylglucosamine
Sphingolipid metabolism ath00600 cpd:C00065 L-Serine
Pyruvate metabolism ath00620 cpd:C00022 Pyruvic acid
Glyoxylate and dicarboxylate metabolism ath00630 cpd:C00160; cpd:C00065 Glycolic acid; L-Serine
Butanoate metabolism ath00650 cpd:C00022 Pyruvic acid
C5-Branched dibasic acid metabolism ath00660 cpd:C00022 Pyruvic acid
Carbon fixation in photosynthetic organisms ath00710 cpd:C00022 Pyruvic acid
Thiamine metabolism ath00730 cpd:C00097; cpd:C00378; cpd:C00022 L-Cysteine; Thiamine; Pyruvic acid
Riboflavin metabolism ath00740 cpd:C00016 FAD
Vitamin B6 metabolism ath00750 cpd:C00314; cpd:C00250 Pyridoxine; Pyridoxal
Nicotinate and nicotinamide metabolism ath00760 cpd:C00006 NADP
Pantothenate and CoA biosynthesis ath00770 cpd:C00429; cpd:C00022; cpd:C00097; cpd:C00106 Dihydrouracil; Pyruvic acid; L-Cysteine; Uracil
Terpenoid backbone biosynthesis ath00900 cpd:C00022 Pyruvic acid
Zeatin biosynthesis ath00908 cpd:C00147 Adenine
Sulfur metabolism ath00920 cpd:C00065; cpd:C00097 L-Serine; L-Cysteine
Isoquinoline alkaloid biosynthesis ath00950 cpd:C01179 4-Hydroxyphenylpyruvic acid
Aminoacyl-tRNA biosynthesis ath00970 cpd:C00152; cpd:C00097; cpd:C00065 L-Asparagine; L-Cysteine; L-Serine

4. Visualization of one of the pathways obtained in the previous point (Vitamin B6)

pathway_analysis(id_metabs_kegg, "ath00750", nodeNames="names", map.zoom=T, nodeTooltip=T)

17 Other Functions

You can convert the absorbance values of the dataset into transmittance values, and vice versa

absorbance_to_transmittance(dataset)

transmittance_to_absorbance(dataset, percent = T)

You can count the number of missing values on the whole dataset, and per sample or variable

count_missing_values(dataset)

count_missing_values_per_sample(dataset)

count_missing_values_per_variable(dataset)

You can get the data matrix on the dataset

get_data(dataset)

You can get the data on the dataset as a data frame

get_data_as_df(dataset)

You can get the values of a variable in a sample

get_data_value(dataset, x.axis.val, sample, by.index = F)

  • dataset: a specmine dataset;

  • x.axis.val: string indicating the name of the variable or numeric value indicating the position (index) of the variable in the data;

  • sample: string indicating the name of the sample or numeric value indicating the position (index) of the sample in the data;

  • by.index: boolean value (TRUE or FALSE) indicating if in the arguments x.axis.val and sample is given the index (by.index=TRUE) or the name of the variable and sample (by.index=FALSE). Defaults to FALSE.

You can all samples’ values in the dataset for a certain variable

get_data_values(dataset, x.axis.val, by.index = FALSE)

  • dataset: a specmine dataset;

  • x.axis.val: string indicating the name of the variable or numeric value indicating the position (index) of the variable in the data;

  • by.index: boolean value (TRUE or FALSE) indicating if in the argument x.axis.val is given the index (by.index=TRUE) or the name of the variable (by.index=FALSE). Defaults to FALSE.

You can get a data frame of the metadata of the dataset

get_metadata(dataset)

You can get the value of a metadata variable in a sample

get_metadata_value(dataset, variable, sample)

  • dataset: a specmine dataset;

  • variable: string indicating the name of the metadata variable or numeric value indicating the position (index) of the metadata variable;

  • sample: string indicating the name of the sample or numeric value indicating the position (index) of the sample;

You can get the samples’ values of a metadata variable

get_metadata_var(dataset, var)

  • dataset: a specmine dataset;

  • variable: string indicating the name of the metadata variable or numeric value indicating the position (index) of the metadata variable.

You can get the names of the samples on the dataset

get_sample_names(dataset)

You can get the type of dataset

get_type(dataset)

You can get the data x values as numbers instead of strings. Only possible if not concentrations data

get_x_values_as_num(dataset)

You can get the data x values as strings

get_x_values_as_text(dataset)

You get know if the dataset in question is spectral or not

is_spectra(dataset)

You can update the metadata in the dataset

set_metadata(dataset, new.metadata)

  • dataset: a specmine dataset;

  • new.metadata: matrix or dataframe of the new metadata.

You can give new names to the samples in the dataset

set_sample_names(dataset, new.sample.names)

  • dataset: a specmine dataset;

  • new.samples.names: character vector with the new names for the samples.

You can set a new label to the yy axis

set_value_label(dataset, new.val.label)

You can set a new label to the xx axis

set_x_label(dataset, new.x.label)

You can give new values to the xx axis

set_x_values(dataset, new.x.values, new.x.label = NULL)

  • dataset: a specmine dataset;

  • new.x.values: numeric or character vector with the new values for the xx axis;

  • new.x.label: string indicating the new label for the xx axis. optional

18 Problems running code: solutions

18.1 “Maximum number of DLLs reached …”

If you receive this error message, you have to increase the maximum number of DLLs R can load by setting the variable R_MAX_NUM_DLLS.

1. Find out where the Renviron or Renviron.site files are located in your machine:

dir(Sys.getenv("R_HOME"),recursive = T,full.names = T,pattern = "Renviron")

2. Add the command R_MAX_NUM_DLLS=150 at the end of one of these files:

  • Example for MACOS:
system(' echo "R_MAX_NUM_DLLS=150" >> /Library/Frameworks/R.framework/Resources/etc/Renviron')
  • Example for UBUNTU:

Command has to be run as administrator. So run r on terminal (type sudo R), insert password and type:

system(' echo "R_MAX_NUM_DLLS=150" >> /usr/lib/R/etc/Renviron')

18.2 If you are getting a problem with rgl package

If you are on linux and having problems using certain functions due to rgl package (“object ‘rgl_clear’ not found”, for example), the problem may be the version of the rgl package you have.

So, performing the following in the terminal may be necessary to use these functions:

sudo apt-get install libglu1-mesa-dev

After this, reinstall the rgl package

install.packages("rgl")

18.3 Error in mvrValstats(object = object, estimate = “train”) : could not find function “mvrValstats”

When runing the function train_models_performance, the above error may arise.

Two possible solutions are:

  • Reinstall caret package, as it might be because you have an older version of this package, where the bug was fixed already;
install.packages("caret")
  • Or, in case the error persists, you have to load the pls package, as a new release of caret package without the bug was not yet provided.
library(pls)
LS0tCnRpdGxlOiAiU3BlY21pbmUgLSBSIHBhY2thZ2UgZm9yIG1ldGFib2xvbWljcyBhbmQgc3BlY3RyYWwgZGF0YSBhbmFseXNpcyBhbmQgbWluaW5nIgphdXRob3I6ICJTYXJhIENhcmRvc28iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjc3M6IHRhYmxlQ29udGVudHMuY3NzCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICMgY29sbGFwc2VkOiBmYWxzZQogICAgZGVwdGg6IDMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAKICAgIHRoZW1lOiBqb3VybmFsIAogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKLS0tCgpgYGB7ciBzZXR1cCwgY2FjaGUgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQpvcHRpb25zKHdpZHRoID0gMTUwLCBkaWdpdHMgPSA0KQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFLCBhdXRvZGVwID0gVFJVRSkgI2F1dG9tYXRpY2FsbHkgY2FjaGUgYW5kIGFzc29jaWF0ZSBvYmplY3RzCmxpYnJhcnkoc3BlY21pbmUpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KVGhpcyBwYWNrYWdlIHByb3ZpZGVzIGEgc2V0IG9mIG1ldGhvZHMgZm9yIG1ldGFib2xvbWljcyBkYXRhIGFuYWx5c2lzLCBpbmNsdWRpbmcgZGF0YSBsb2FkaW5nIGluIGRpZmZlcmVudCBmb3JtYXRzLCBwcmUtcHJvY2Vzc2luZywgbWV0YWJvbGl0ZSBpZGVudGlmaWNhdGlvbiwgdW5pdmFyaWF0ZSBhbmQgbXVsdGl2YXJpYXRlCWRhdGEgYW5hbHlzaXMsIG1hY2hpbmUgbGVhcm5pbmcsIGZlYXR1cmUgc2VsZWN0aW9uIGFuZCBwYXRod2F5IGFuYWx5c2lzLgoKQSB3ZWIgdmVyc2lvbiwgW1dlYlNwZWNtaW5lXSh3d3cud2Vic3BlY21pbmUucm9zYWxpbmQuZGkudW1pbmhvLnB0KSwgaXMgbm93IGF2YWlsYWJsZSwgaW4gY2FzZSB5b3UgZG8gbm90IGhhdmUgbXVjaCBza2lsbHMgaW4gdGhlIFIgZW52aXJvbm1lbnQgb3IgcHJvZ3JhbW1pbmcsIHdpdGggYSBlYXN5LXRvLXVzZSBpbnRlcmZhY2UsIHdpdGggdGhlIGZlYXR1cmVzIGltcGxlbWVudGVkIGluIHRoaXMgcGFja2FnZSBhbmQgYSBwdWJsaWMgcmVwb3NpdG9yeSB3aXRoIG1ldGFib2xvbWljcyBkYXRhICh5b3UgY2FuIGFsc28gc2F2ZSB5b3VyIG93biBkYXRhLCBwcml2YXRlIG9yIG5vdCwgaWYgeW91IGNyZWF0ZSBhbiBhY2NvdW50KS4gVGhlIF9zcGVjbWluZV8gUiBwYWNrYWdlIG1heSBiZSBiZXR0ZXIgdG8gdXNlLCBpZiB3YW50ZWQgYSBtb3JlIGZsZXhpYmxlIHVzZSBvZiB0aGUgcHJvdmlkZWQgZmVhdHVyZXMgYW5kIHVzZSBpdCB3aXRoIG90aGVyIFIgdG9vbHMuCjwvcD4KCgojIFBhY2thZ2UgaW5zdGFsYXRpb24KCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KU28gdGhhdCB0aGUgcGFja2FnZSBjYW4gYmUgZnVsbHkgdXNlZCBhbmQgaW5zdGFsbGVkIGNvcnJlY3RseSwgc29tZSBSIHBhY2thZ2VzIGhhdmUgdG8gYmUgaW5zdGFsbGVkIGJlZm9yZSBpbnN0YWxsaW5nICpzcGVjbWluZSoKPC9wPgoKIyMgSW5zdGFsbGluZyBuZWNlc3NhcnkgQ1JBTiBwYWNrYWdlcwoKIyMjIE5lY2Vzc2FyeSBwYWNrYWdlcyB0aGF0IG1heSBub3QgYmUgaW5zdGFsbGVkCgpgYGB7ciBldmFsPUZ9Cmluc3RhbGwucGFja2FnZXMoYygnbGF0dGljZScsICdnZ3Bsb3QyJywgJ2NhcmV0JywgJ0JyYWRsZXlUZXJyeTInLCAnZTEwNzEnLCAnZWFydGgnLAogICAgICAgICAgICAgICAgICAgJ2Zhc3RJQ0EnLCdnYW0nLCAnaXByZWQnLCAna2VybmxhYicsICdrbGFSJywgJ01BU1MnLCAnZWxsaXBzZScsICdtZGEnLAogICAgICAgICAgICAgICAgICAgJ21nY3YnLCAnbWxiZW5jaCcsJ01MbWV0cmljcycsICdubmV0JywgJ3BhcnR5JywgJ3BscycsICdwUk9DJywgJ3Byb3h5JywKICAgICAgICAgICAgICAgICAgICdyYW5kb21Gb3Jlc3QnLCAnUkFOTicsJ3NwbHMnLCAnc3Vic2VsZWN0JywgJ3BhbXInLCAnc3VwZXJwYycsICdDdWJpc3QnLAogICAgICAgICAgICAgICAgICAgJ3Rlc3R0aGF0JywgJ2lncmFwaCcsJ1J3ZWthJywgJ3N0YXRzJywgJ3NjYXR0ZXJwbG90M2QnLCAnY29tcGFyZScsCiAgICAgICAgICAgICAgICAgICAnaHlwZXJTcGVjJywgJ0NoZW1vU3BlYycsICdiYXNlbGluZScsJ3JnbCcsICdNZXRyaWNzJywgJ0dHYWxseScsCiAgICAgICAgICAgICAgICAgICAnZ2dkZW5kcm8nLCAncGNhUFAnLCAnUkNvbG9yQnJld2VyJywgJ2dyaWQnLCAnbWV0aG9kcycsICdxZGFwJywKICAgICAgICAgICAgICAgICAgICdzaGlueWRhc2hib2FyZCcsICdzaGlueUJTJywgJ3NoaW55anMnLCAnRFQnLCAnUk15U1FMJywgJ2JjcnlwdCcsCiAgICAgICAgICAgICAgICAgICAnb3BlbnNzbCcsICdHR2FsbHknLCdzaGlueVdpZGdldHMnLCAnY29sb3VycGlja2VyJywgJ2RldnRvb2xzJywKICAgICAgICAgICAgICAgICAgICdNTG1ldHJpY3MnLCAnc3BlYXEnKSkKYGBgCgojIyBJbnN0YWxsaW5nIG5lY2Vzc2FyeSBCaW9Db25kdWN0b3IgcGFja2FnZXMKCl9fMTogSWYgYmlvY29uZHVjdG9yIGlzIG5vdCB5ZXQgaW5zdGFsbGVkOl9fCgpgYGB7ciBldmFsPUZ9CnNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQpiaW9jTGl0ZSgpCmBgYAoKX18yOiBCaW9jb25kdWN0b3IgcGFja2FnZXMgbmVjZXNzYXJ5Ol9fCgpgYGB7ciBldmFsPUZ9CnNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQpiaW9jTGl0ZSgiaW1wdXRlIiwgImdlbmVmaWx0ZXIiLCAieGNtcyIsICJNQUlUIiwgIktFR0dSRVNUIiwgIktFR0dncmFwaCIsICJtelIiKQpgYGAKCgojIyBJbnN0YWxsaW5nIG90aGVyIHBhY2thZ2VzCgpgYGB7ciBldmFsPUZ9CmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YignY3l0b3NjYXBlL3ItY3l0b3NjYXBlLmpzJykKYGBgCgojIyBJbnN0YWxsaW5nICpzcGVjbWluZSoKCiMjIyBDUkFOIHZlcnNpb24KCmBgYHtyIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcygic3BlY21pbmUiKQpgYGAKCiMjIyBEZXZlbG9wbWVudCB2ZXJzaW9uCgoqKk5vdGUgdGhhdCwgYXQgc29tZSBwb2ludCwgdGhlIGRldmVsb3BtZW50IHZlcnNpb24gbWF5IG5vdCBiZSBjb25wbGV0ZWx5IGVycm9yIGZyZWUuKioKCmBgYHtyIGV2YWw9Rn0KZGV2dG9vbHM6Omluc3RhbGxfYml0YnVja2V0KCdjaHJpc2JjbC9tZXRhYm9sb21pY3NwYWNrYWdlJywgcmVmPSdtYXN0ZXInKQpgYGAKCgoKCgojIFN1cHBvcnRlZCBEYXRhCgo8cCBzdHlsZSA9ICd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+ClZhcmlvdXMgdHlwZXMgb2YgZGF0YSBhcmUgc3VwcG9ydGVkLCBpbiBtYW55IGZvcm1hdHMuIFRoZSB3ZWJzaXRlIGNvbnNpZGVycyB0aGF0ICoqZWFjaCBkYXRhIGZpbGUgcmVwcmVzZW50cyBvbmUgZGlzdGluY3Qgc2FtcGxlLCB3aXRoIGV4Y2VwdGlvbiBmb3Igd2hlbiBvbmUgY3N2IGZpbGUgb2YgVVYtVklTLCBJUiBhbmQgUmFtYW4gU3BlY3RyYSBpcyBnaXZlbiBhbmQgZm9yIHRoZSBkYXRhIGZpbGUgb2YgY29uY2VudHJhdGlvbnMgZGF0YSoqLgo8L3A+CgojIyBOTVIgYW5kIEdDL0xDLU1TIFBlYWsgTGlzdHMKCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KVGhlIHBlYWsgbGlzdHMgZGF0YSBmaWxlcyBtdXN0IGhhdmUgdGhlICoqQ1NWIGZvcm1hdCoqLiBFYWNoIENTViBmaWxlIG11c3QgcmVwcmVzZW50IGEgc2FtcGxlIGFuZCBoYXZlIHR3byBjb2x1bW5zOiB0aGUgZmlyc3Qgb25lIGNvcnJlc3BvbmRzIHRvIHRoZSBjaGVtaWNhbCBzaGlmdHMgKGluIHBwbXMpIG9yIHRoZSBtYXNzL2NoYXJnZSByYXRpb3MgYW5kIHRoZSBzZWNvbmQgb25lIHRoZSBpbnRlbnNpdGllcyBvZiB0aG9zZSBwZWFrcy4KClBhcnQgb2YgYSBDU1YgZmlsZSAqKmV4YW1wbGUqKiBvZiBhIHBlYWsgbGlzdDoKPC9wPgoKPHAgc3R5bGUgPSAndGV4dC1hbGlnbjogY2VudGVyOyc+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcG0saW50ZW5zaXR5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjc0LDAuMDAwMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC44OSwwLjAwMDQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuOTAsMC4wMDA3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjkxLDAuMDAwNQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC45MSwwLjAwMDgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuOTIsMC4wMDA0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjk0LDAuMDAwMwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC45NSwwLjAwMDQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuOTYsMC4wMDA5CjwvcD4KCgojIyBOTVIgU3BlY3RyYQoKPHAgc3R5bGUgPSAndGV4dC1hbGlnbjoganVzdGlmeTsnPgpUaGVyZSBhcmUgdHdvIG5tciBzcGVjdHJhIGZvcm1hdHMgdGhhdCBhcmUgc3VwcG9ydGVkLgoKPGEgbmFtZT0iYnJ1a2VyX2Zvcm1hdCI+PC9hPgpUaGUgX19CUlVLRVJfXyBmb3JtYXQgaXMgc3VwcG9ydGVkLCBpZiB0aGUgX3Byb2Nlc3NlZCBzcGVjdHJhXyBhcmUgZ2l2ZW4uIEVhY2ggc3BlY3RydW0gZGF0YSBoYXMgdG8gYmUgaW4gYSBkaWZmZXJlbnQgZm9sZGVyLiBfX0VhY2ggZm9sZGVyIGhhcyB0byBoYXZlIHRoZSBmb2xsb3dpbmcgc3RydWN0dXJlOl9fCgpfQXQgbGVhc3QgdGhlIGZpbGVzIF9wcm9jc18gYW5kIF8xcl8gaGF2ZSB0byBiZSBwcmVzZW50LiBUaGV5IGhhdmUgdG8gYmUgaW5zaWRlIF9zcGVjdHJ1bWZvbGRlcm5hbWUvcGRhdGEvMV8gXwoKPHAgc3R5bGUgPSAndGV4dC1hbGlnbjogY2VudGVyOyc+CiFbXSgvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMvc3BlY21pbmUvcmVwb3J0cy9icnVrZXJfZm9ybWF0LnBuZykKPC9wPgoKPGEgbmFtZT0idmFyaWFuX2Zvcm1hdCI+PC9hPgpUaGUgX19WQVJJQU5fXyBmb3JtYXQgaXMgc3VwcG9ydGVkLCBvbmx5IGlmIHRoZSByYXcgX2ZpZF8gZmlsZSBpcyBnaXZlbiwgYWxvbmdzaWRlIHdpdGggdGhlIF9wcm9jcGFyXyBmaWxlLiBFYWNoIHNwZWN0cnVtIGRhdGEgaGFzIHRvIGJlIGluIGEgZGlmZmVyZW50IGZvbGRlci4gX19FYWNoIGZvbGRlciBoYXMgdG8gaGF2ZSB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpfXwoKPHAgc3R5bGUgPSAndGV4dC1hbGlnbjogY2VudGVyOyc+CiFbXSgvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMvc3BlY21pbmUvcmVwb3J0cy92YXJpYW5fZm9ybWF0LnBuZykKPC9wPgoKPC9wPgoKCiMjIEdDL0xDLU1TIFNwZWN0cmEKCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KVGhlIE1TIHNwZWN0cmFsIGRhdGEgZmlsZXMgbXVzdCBlaXRoZXIgaGF2ZSAqKi5telhNTCoqLCAqKi5uZXRDREYqKiBvciAqKm16RGF0YSoqIGZvcm1hdHMuCjwvcD4KCiMjIFVWLVZpcywgSVIgYW5kIFJhbWFuIFNwZWN0cmEKCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KVGhlIGRhdGEgZmlsZXMgb2YgdGhlc2UgdHlwZSBvZiBzcGVjdHJhIG11c3QgYmUgaW4gb25lIG9mIHRoZSBmb2xsb3dpbmcgZm9ybWF0czogQ1NWLCAoSilEWCwgU1BDIG9yIE1TIEVYQ0VMICgueGxzeCkuCgpGb3IgZGF0YSBpbiAqKk1TIEVYQ0VMIG9yIENTViBmaWxlcyoqLCBlYWNoIGZpbGUgbXVzdCBoYXZlIHR3byBjb2x1bW5zOiB0aGUgZmlyc3Qgb25lIHJlcHJlc2VudGluZyB0aGUgd2F2ZW51bWJlciwgd2F2ZWxlbmd0aCBvciByYW1hbiBzaGlmdCwgYWNjb3JkaW5nIHRvIHRoZSB0eXBlIG9mIHNwZWN0cmEsIGFuZCB0aGUgc2Vjb25kIG9uZSB0aGUgdmFsdWUgb2YgdGhlIG1lYXN1cmVtZW50cy4KCgoKV2hlbiBvbmx5ICoqb25lIENTViBmaWxlKiogaXMgZ2l2ZW4sIHRoZSBzdHJ1Y3R1cmUgYXMgdG8gYmUgc2ltaWxhciB0byB0aGUgZm9sbG93aW5nIGV4YW1wbGUgKHRoZSBmaXJzdCBjb2x1bW4gY29ycmVzcG9uZHMgdG8gdGhlIHdhdmVudW1iZXIsIHdhdmVsZW5ndGggb3IgcmFtYW4gc2hpZnQsIGFjY29yZGluZyB0byB0aGUgdHlwZSBvZiBzcGVjdHJhKToKCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGNlbnRlcjsnPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxzYW1wbGVOYW1lMSxzYW1wbGVOYW1lMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIwMCwwLjA4NTk1NjY0OCwwLjA0ODMwNDY4CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjAxLDAuMDY3MTgyNjI3LDAuMDE3MzE2MzU5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjAyLDAuMDQ0ODQyMjIzLDAuMDI2OTMwNjMzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjAzLDAuMDUxMzM1OTYzLDAuMDQxNTM5NDMxCjwvcD4KCjwvcD4KCiMjIENvbmNlbnRyYXRpb25zIERhdGEKCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KQ29uY2VudHJhdGlvbnMgZGF0YSBtdXN0IGJlIGEgKipDU1Ygb3IgVFNWIGZpbGUqKiB3aXRoIHRoZSBzYW1wbGVzIG5hbWVzIGluIHRoZSBmaXJzdCBjb2x1bW4gKGVhY2ggbGluZSB0aGVuIGNvcnJlc3BvbmRzIHRvIGEgc2FtcGxlKSBhbmQgdGhlIGNvbmNlbnRyYXRpb25zIHZhbHVlcyBmb3IgZWFjaCBtZXRhYm9saXRlIGluIHRoZSBmb2xsb3dpbmcgY29sdW1ucy4gQWx0ZXJuYXRpdmVseSwgc2FtcGxlcyBuYW1lcyBjYW4gYmUgaW4gdGhlIGZpcnN0IGxpbmUgKGVhY2ggY29sdW1uIHRoZW4gY29ycmVzcG9uZHMgdG8gYSBzYW1wbGUpIGFuZCB0aGUgY29uY2VudHJhdGlvbnMgdmFsdWVzIGZvciBlYWNoIG1ldGFib2xpdGUgaW4gdGhlIGZvbGxvd2luZyBsaW5lcy4KClBhcnQgb2YgYSBDU1YgZXhhbXBsZSBmaWxlIG9mIGNvbmNlbnRyYXRpb25zIGZpbGU6Cgo8cCBzdHlsZSA9ICd0ZXh0LWFsaWduOiBjZW50ZXI7Jz4KCiAgICAgICAgICAgICAgICAgICAgUGF0aWVudCBJRCwxLjYtQW5oeWRyby1iZXRhLUQtZ2x1Y29zZSwxLU1ldGh5bG5pY290aW5hbWlkZSwyLUFtaW5vYnV0eXJhdGUKICAgICAgICAgICAgICAgICAgICBQSUZfMTc4LDQwLjg1LDY1LjM3LDE4LjczCiAgICAgICAgICAgICAgICAgICAgUElGXzA4Nyw2Mi4xOCwzNDAuMzYsMjQuMjkKICAgICAgICAgICAgICAgICAgICBQSUZfMDkwLDI3MC40Myw2NC43MiwxMi4xOAogICAgICAgICAgICAgICAgICAgIE5FVExfMDA1X1YxLDE1NC40Nyw1Mi45OCwxNzIuNDMKICAgICAgICAgICAgICAgICAgICBQSUZfMTE1LDIyLjIsNzMuNywxNS42NAoKPC9wPgoKPC9wPgoKCiMjIE1ldGFkYXRhIEZpbGUKCjxwIHN0eWxlID0gJ3RleHQtYWxpZ246IGp1c3RpZnk7Jz4KQXMgcmVnYXJkcyB0byB0aGUgbWV0YWRhdGEgZmlsZSwgaXQgY2FuIGVpdGhlciBoYXZlIENTViBvciBUU1YgZm9ybWF0LiBFYWNoIGxpbmUgc2hvdWxkIGNvcnJlc3BvbmQgdG8gYSBzYW1wbGUsIHdoZXJlIHRoZSBmaXJzdCBjb2x1bW4gcmVwcmVzZW50cyB0aGUgbmFtZXMgb2Ygc3VjaCBzYW1wbGVzLCBhbmQgdGhlIHJlbWFpbmluZyBvbmVzIHRoZSBtZXRhZGF0YSBjbGFzc2VzLgoKVGhlIGZpcnN0IGNvbHVtbiBjb3JyZXNwb25kcyB0byB0aGUgbmFtZXMgb2YgdGhlIHNhbXBsZXMuICoqRm9yIHRoZSBjYXNlcyB3aGVyZSBtb3JlIHRoYW4gb25lIGRhdGEgZmlsZSBpcyBnaXZlbiwgdGhlIG5hbWVzIG9mIHRoZSBzYW1wbGVzIGhhdmUgdG8gY29ycmVzcG9uZCB0byB0aGUgbmFtZXMgb2YgdGhlIGRhdGEgZmlsZXMuKioKCkhlcmUgeW91IGhhdmUgYW4gKipleGFtcGxlKiogb2YgYSBtZXRhZGF0YSBmaWxlOgoKPHAgc3R5bGUgPSAndGV4dC1hbGlnbjogY2VudGVyOyc+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlIE5hbWUsU2Vhc29ucwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEp1bHkyMDEwLFdpbnRlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNlcHRlbWJlcjIwMTAsU3ByaW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT2N0b2JlcjIwMTAsU3ByaW5nCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTm92ZW1iZXIyMDEwLFNwcmluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZlYnJ1YXJ5MjAxMSxTdW0vQXV0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWFyY2gyMDExLFN1bS9BdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBcHJpbDIwMTEsU3VtL0F1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heTIwMTEsU3VtL0F1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEp1bmUyMDExLFdpbnRlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEp1bHkyMDExLFdpbnRlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEF1Z3VzdDIwMTEsV2ludGVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2VwdGVtYmVyMjAxMSxTcHJpbmcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPY3RvYmVyMjAxMSxTcHJpbmcKCjwvcD4KPC9wPgoKCgoKCiMgUmVhZCBkYXRhIGludG8gc3BlY21pbmUKClRoZSBkYXRhIGZpbGVzIHVzZWQgaW4gdGhlIGV4YW1wbGVzIGRlY3JpYmVkIGluIHRoaXMgc2VjdGlvbiBjYW4gYmUgb2J0YWluZWQgaW4gW2hlcmVdKGh0dHA6Ly9kYXJ3aW4uZGkudW1pbmhvLnB0L21ldGFib2xvbWljc3BhY2thZ2UvaW5kZXguaHRtbCkuCgojI1N0cnVjdHVyZSBvZiBhIHNwZWNtaW5lIGRhdGFzZXQKCkEgc3BlY21pbmUgZGF0YXNldCBpcyBhIGxpc3Qgd2l0aCB0aGUgZm9sbG93aW5nIGVsZW1lbnRzOgoKICAqIF9kYXRhXzogZGF0YSBmcmFtZSBvZiB0aGUgZGF0YSBwb2ludHMuIEVhY2ggY29sdW1uIGNvcnJlc3BvbmRzIHRvIGEgc2FtcGxlcyBhbmQgZWFjaCBsaW5lIHRvIGEgZGF0YSB2YXJpYWJsZS4gVGhlIHZhbHVlcyBpbiBlYWNoIGNlbGwgYXJlIHRoZSB5eSB2YWx1ZXMgb2Ygc2FpZCB2YXJpYWJsZSBpbiBzYWlkIHNhbXBsZTsKICAKICAqIF9tZXRhZGF0YV86IGRhdGEgZnJhbWUgb2YgdGhlIG1ldGFkYXRhIGluZm9ybWF0aW9uLiBFYWNoIGNvbHVtbiBjb3JyZXNwb25kcyB0byBhIG1ldGFkYXRhIHZhcmlhYmxlIGFuZCBlYWNoIGxpbmUgdG8gYSBzYW1wbGVzLiBUaGUgdmFsdWVzIGluIGVhY2ggY2VsbCBhcmUgdGhlIHZhbHVlcyBvZiBzYWlkIHZhcmlhYmxlIGluIHNhaWQgc2FtcGxlOwogIAogICogX3R5cGVfOiBzdHJpbmcgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBkYXRhLiBJdCBjYW4gZWl0aGVyIGJlICJubXItcGVha3MiLCAibm1yLXNwZWN0cmEiLCAibGNtcy1wZWFrcyIsICJnY21zLXBlYWtzIiwgImxjbXMtc3BlY3RyYSIsICJnY21zLXNwZWN0cmEiLCAiaXItc3BlY3RyYSIsICJ1dnYtc3BlY3RyYSIsICJyYW1hbi1zcGVjdHJhIiwgImludGVncmF0ZWQtZGF0YSIsICJjb25jZW50cmF0aW9ucyIsICJ1bmRlZmluZWQiOwogIAogICogX2Rlc2NyaXB0aW9uXzogYSBzaG9ydCBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YTsKICAKICAqIF9sYWJlbHNfOiBsaXN0IHdpdGggdGhlIGZvbGxvd2luZyBlbGVtZW50cwogICAgICAKICAgICAgKyBfeF86IHh4IGF4aXMgbGFiZWxzOwogICAgICAKICAgICAgKyBfdmFsXzogeXkgYXhpcyBsYWJlbHMuCgojIyBOTVIgYW5kIEdDL0xDLU1TIFBlYWsgTGlzdHMgKGluY2x1ZGluZyBwZWFrIGFsaWdubWVudCkKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgpfXzE6IFJlYWQgTk1SIGFuZCBNUyBQZWFrIGxpc3RzIGRhdGEgaW50byBsaXN0IG9mIGRhdGEgc2FtcGxlc19fCgo+IHJlYWRcX2NzdnNcX2ZvbGRlcihmb2xkZXJuYW1lLCBoZWFkZXI9VFJVRSwgc2VwPSIsIiwgZGVjPSIuIiwgLi4uKQoKICAqIF9mb2xkZXJuYW1lXzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2YgdGhlIGRhdGEgZm9sZGVyOwogIAogICogX2hlYWRlcl86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciBkYXRhIGZpbGVzIGhhdmUgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWVzIG9mIHRoZSBkYXRhIHZhcmlhYmxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9zZXBfOiB0aGUgc2VwYXJhdG9yIGNoYXJhY3RlciBvZiB0aGUgZGF0YSB2YWx1ZXMuIERlZmF1bHRzIHRvICIsIjsKICAKICAqIF9kZWNfOiBjaGFyYWN0ZXIgdXNlZCBpbiB0aGUgZmlsZSBmb3IgZGVjaW1hbCBwb2ludHMuIERlZmF1bHRzIHRvICIuIjsKICAKICAqIF8uLi5fOiBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgZm9yIFtyZWFkLmNzdl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3V0aWxzL3ZlcnNpb25zLzMuNC4xL3RvcGljcy9yZWFkLnRhYmxlKSBmdW5jdGlvbiBmcm9tIFt1dGlsc10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3V0aWxzL3ZlcnNpb25zLzMuNC4xKSBwYWNrYWdlLgogIAogIApfXzI6IFJlYWQgbWV0YWRhdGEgZmlsZSAob3B0aW9uYWwgc3RlcCBidXQgcmVjb21lbmRlZClfXwoKPiByZWFkXF9tZXRhZGF0YShmaWxlbmFtZSwgaGVhZGVyLmNvbCA9IFQsIGhlYWRlci5yb3cgPSBULCBzZXAgPSAiLCIpCgogICogX2ZpbGVuYW1lCV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwYXRoIG9mIHRoZSBmaWxlIHdpdGggdGhlIG1ldGFkYXRhOwogIAogICogX2hlYWRlci5jb2xfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBDU1YgZmlsZSBjb250YWlucyBhIGhlYWRlciBjb2x1bW4gd2l0aCB0aGUgbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGVzLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2hlYWRlci5yb3dfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBDU1YgZmlsZSBjb250YWlucyBhIGhlYWRlciByb3cgd2l0aCB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKCiAgKiBfc2VwXzogdGhlIHNlcGFyYXRvciBjaGFyYWN0ZXIuIERlZmF1bHRzIHRvICIsIi4KCl9fMzogUGVyZm9ybSBQZWFrIEFsaWdubWVudCwgaW50byBzcGVjbWluZSBkYXRhc2V0X18KCj4gZ3JvdXBcX3BlYWtzKHNhbXBsZS5saXN0LCB0eXBlLCBtZXRob2QgPSAib3duIiwgbWV0YWRhdGEgPSBOVUxMLCBzYW1wLmNsYXNzZXMgPSAxLCBkZXNjcmlwdGlvbiA9ICIiLCBsYWJlbC54ID0gTlVMTCwgbGFiZWwudmFsdWVzID0gTlVMTCwgc3RlcCA9IDAuMDMpCgogICogX3NhbXBsZS5saXN0XzogbGlzdCBjb250YWluaW5nIHRoZSBzYW1wbGUncyBkYXRhLiBUaGlzIGxpc3QgY2FuIGJlIG9idGFpbmVkIGZyb20gdGhlIGZ1bmN0aW9uIF9yZWFkXF9jc3ZzXF9mb2xkZXJfIGFib3ZlOwoKICAqIF90eXBlXzogdHlwZSBvZiB0aGUgZGF0YS4gQ2FuIGVpdGhlciBiZSAibm1yLXBlYWtzIiwgImxjbXMtcGVha3MiIG9yICJnY21zLXBlYWtzIjsKICAKICAqIF9tZXRob2RfOiBtZXRob2Qgb2YgcGVhayBhbGlnbm1lbnQuIENhbiBlaXRoZXIgYmUKICAKICAgICAgKyAib3duIjogU3BlY21pbmUgbWV0aG9kLiBEZWZhdWx0IHZhbHVlOwogICAgICAKICAgICAgKyAibWV0YWJvYW5hbHlzdCI6IE1ldGFib0FuYWx5c3QgbWV0aG9kLCB3aGljaCBpcyBmb3IgdXNpbmcgdGhlIHBlYWsgYWxpZ25tZW50IHVzZWQgaW4gTWV0YWJvQW5hbHlzdCBzb2Z0d2FyZS4KICAKICAqIF9tZXRhZGF0YV86IGRhdGEgZnJhbWUgY29udGFpbmluZyB0aGUgbWV0YWRhdGEuIENhbiBiZSBvYnRhaW5lZCBmcm9tIHRoZSBmdW5jdGlvbiBfcmVhZFxfbWV0YWRhdGFfIGFib3ZlOyBfX29wdGlvbmFsIGJ1dCByZWNvbWVuZGVkX18KICAKICAqIF9zYW1wLmNsYXNzZXNfOiBzdHJpbmcgY29udGFpbmcgdGhlIG1ldGFkYXRhJ3MgdmFyaWFibGUgbmFtZSB0byBiZSB1c2VkIGluIHRoZSBNZXRhYm9BbmFseXN0IG1ldGhvZC4gQ2FuIGJlIG9idGFpbmVkIGZyb20gY29sbmFtZXMobWV0YWRhdGFfZGF0YWZyYW1lKS4gRGVmYXVsdHMgdG8gdGhlIHZhcmlhYmxlIHJlcHJlc2VudGVkIGJ5IHRoZSBmaXJzdCBjb2x1bW4uCgogICogX2Rlc2NyaXB0aW9uXzogc2hvcnQgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGEuIF9fb3B0aW9uYWxfXwogIAogICogX2xhYmVsLnhfOiB0aGUgbGFiZWwgZm9yIHRoZSB4IHZhbHVlcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwudmFsdWVzXzogdGhlIGxhYmVsIGZvciB0aGUgeSB2YWx1ZXMuIF9fb3B0aW9uYWxfXwogIAogICogX3N0ZXBfOiBzdGVwIHZhbHVlIGZvciB0aGUgcGVhayBhbGlnbm1lbnQgcHJvY2VzcyBpbiB0aGUgc3BlY21pbmUgbWV0aG9kLiBEZWZhdWx0cyB0byAwLjAzLgoKIyMjIEV4YW1wbGUgPGEgbmFtZT0icmVhZF9ubXJfcGVha3NfZXgiPjwvYT4KCl9fMS5fXyBTdHJpbmdzIGluZGljYXRpbmcgd2hlcmUgZGF0YSBhbmQgbWV0YWRhdGEgaXM6CgpgYGB7cn0Kbm1yX3BlYWtzX2xpc3RzX2RhdGFfZm9sZGVyPSIvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMvbWV0YWJvbG9taWNzX2RhdGFzZXRzL05NUi9QZWFrIGxpc3RzL3Byb3BvbGlzL1Byb3BvbGlzX05NUiBEYXRhX2RhdGEiCm5tcl9wZWFrc19saXN0c19tZXRhZGF0YV9maWxlPSIvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMvbWV0YWJvbG9taWNzX2RhdGFzZXRzL05NUi9QZWFrIGxpc3RzL3Byb3BvbGlzL3Byb3BvbGlzX25tcl9tZXRhZGF0YS5jc3YiCmBgYAoKX18yLl9fIFJlYWQgZGF0YSBmb2xkZXI6CgpgYGB7cn0Kbm1yX3BlYWtzX2xpc3Q9cmVhZF9jc3ZzX2ZvbGRlcihubXJfcGVha3NfbGlzdHNfZGF0YV9mb2xkZXIpCmBgYAoKX18zLl9fIFJlYWQgbWV0YWRhdGEgZmlsZToKCmBgYHtyfQpubXJfcGVha3NfbWV0YWRhdGE9cmVhZF9tZXRhZGF0YShubXJfcGVha3NfbGlzdHNfbWV0YWRhdGFfZmlsZSkKYGBgCgpfXzQuX18gQWxpZ24gcGVha3MgdXNpbmcgc3BlY21pbmUgbWV0aG9kIHdpdGggYSBzdGVwIG9mIDAuMDMgKGRlZmF1bHQgdmFsdWVzLCBzbyBpdCBpcyBub3QgbmVjZXNzYXJ5IHRvIGRlZmluZSB0aGVtKSwgd2hpY2ggd2lsbCBub3cgcmV0dXJuIHRoZSBzcGVjbWluZSBkYXRhc2V0OgoKYGBge3J9Cm5tcl9wZWFrc19kYXRhc2V0PWdyb3VwX3BlYWtzKG5tcl9wZWFrc19saXN0LCAibm1yLXBlYWtzIiwgbWV0YWRhdGEgPSBubXJfcGVha3NfbWV0YWRhdGEsIGRlc2NyaXB0aW9uPSJwcm9wb2xpcyBubXIgc2FtcGxlcyIsIGxhYmVsLnggPSAicHBtIiwgbGFiZWwudmFsdWVzID0gImludGVuc2l0eSIpCmBgYAoKX181Ll9fIFRhYmxlIG9mIHRoZSBkYXRhIGp1c3QgbG9hZGVkIChpdCBpcyBwb3NzaWJsZSB0byByZWFsaXplIHRoYXQgdGhlcmUgYXJlIG1pc3NpbmcgdmFsdWVzKToKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKG5tcl9wZWFrc19kYXRhc2V0JGRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCmBgYAoKX182Ll9fIFRhYmxlIG9mIHRoZSBtZXRhZGF0YSBqdXN0IGxvYWRlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKG5tcl9wZWFrc19kYXRhc2V0JG1ldGFkYXRhKQpgYGAKCgojIyBOTVIgU3BlY3RyYSAocGVhayBkZXRlY3Rpb24gaW5kZXBlbmRlbnQgb2YgZGF0YSByZWFkaW5nKQoKIyMjIEZ1bmN0aW9ucyB0byB1c2UKCiMjIyMgV2hlbiB0aGUgZGF0YSBpcyBpbiBbQlJVS0VSXSgjYnJ1a2VyX2Zvcm1hdCkgZm9ybWF0Cgo+IHJlYWRfQnJ1a2VyX2ZpbGVzKGJydWtlcl9kaXJlY3RvcnksIG1ldGFkYXRhX2ZpbGU9TlVMTCwgbS5oZWFkZXJfY29sPVQsIG0uaGVhZGVyX3Jvdz1ULCBtLnNlcD0iLCIsCiAgICAgICAgICAgICAgICAgIHNhbXBsZXMubmFtZXM9TlVMTCwgemlwcGVkPVQsIGRlc2NyaXB0aW9uPSIiLCBsYWJlbC54PSJwcG0iLCBsYWJlbC52YWx1ZXM9ImludGVuc2l0eSIpCgogICogX2JydWtlclxfZGlyZWN0b3J5Xzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2YgdGhlIGRhdGEgZm9sZGVyIHdpdGggYWxsIHRoZSBzcGVjdHJhIGZvbGRlcnM7CiAgCiAgKiBfbWV0YWRhdGFcX2ZpbGVfOiBzdHJpbmcgY29udGFpbmluZyB0aGUgcGF0aCBvZiB0aGUgbWV0YWRhdGEgZmlsZTsgX19vcHRpb25hbCBidXQgcmVjb21lbmRlZF9fCiAgCiAgKiBfbS5oZWFkZXJcX2NvbF86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgY29sdW1uIHdpdGggdGhlIG5hbWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9tLmhlYWRlclxfcm93XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgZmlsZSBjb250YWlucyBhIGhlYWRlciByb3cgd2l0aCB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9tLnNlcF86IHRoZSBzZXBhcmF0b3IgY2hhcmFjdGVyIG9mIHRoZSBtZXRhZGF0YSBmaWxlLiBEZWZhdWx0cyB0byAiLCI7CiAgCiAgKiBfc2FtcGxlcy5uYW1lc186IENTViBmaWxlIHdoZXJlIHRoZSBmaXJzdCBjb2x1bW4gcmVwcmVzZW50cyB0aGUgc2FtcGxlcyBuYW1lcyBhbmQgaW4gdGhlIHNlY29uZCBjb2x1bW4gdGhlIG5hbWVzIG9mIHRoZSBzcGVjdHJhIGRpcmVjdG9yaWVzIHRvIHdoaWNoIHRoZXkgY29ycmVzcG9uZC4gSWYgTlVMTCwgaXQgd2lsbCBiZSBjb25zaWRlcmVkIHRoYXQgdGhlIGRpcmVjdG9yaWVzIG5hbWVzIGFyZSB0aGUgc2FtcGxlcyBuYW1lcyAoaXQgaGFzIHRvIGJlIHRoZSBzYW1lIG5hbWVzIHRoYXQgYXBwZWFyIGluIHRoZSBtZXRhZGF0YSBmaWxlKTsKICAKICAqIF96aXBwZWRfOiBCb2xlZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBzcGVjdHJhIGRpcmVjdG9yaWVzIGFyZSB6aXBwZWQgKC56aXApIG9yIG5vdC4gX19UaGUgY29tcHJlc3NlZCBmaWxlcyBtdXN0IGhhdmUgdGhlIGV4dGVuc2lvbiAuemlwLiBJZiBub3QsIHlvdSB3aWxsIGhhdmUgdG8gdW5jb21wcmVzcyB0aGVtIHlvdXJzZWxmLl9fCiAgCiAgKiBfZGVzY3JpcHRpb25fOiBhIHNob3J0IHRleHQgZGVzY3JpYmluZyB0aGUgZGF0YXNldC4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwueF86IHRoZSBsYWJlbCBmb3IgdGhlIHggdmFsdWVzLiBfX29wdGlvbmFsX18KICAKICAqIF9sYWJlbC52YWx1ZXNfOiB0aGUgbGFiZWwgZm9yIHRoZSB5IHZhbHVlcy4gX19vcHRpb25hbF9fCgoKIyMjIyBXaGVuIHRoZSBkYXRhIGlzIFtWQVJJQU5dKCN2YXJpYW5fZm9ybWF0KSBmb3JtYXQKCl9UTyBVU0UgVEhJUyBGVU5DVElPTiBZT1UgTVVTVCBIQVZFIFBZVEhPTjMgSU5TVEFMTEVELCBXSVRIIFRIRSBNT0RVTEUgbm1yZ2x1ZSBJTlNUQUxMRURfCgo+IHJlYWRfdmFyaWFuX3NwZWN0cmFfcmF3KHZhcmlhbl9zcGVjdHJhX2RpcmVjdG9yeSwKICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YV9maWxlPU5VTEwsIG0uaGVhZGVyX2NvbD1ULCBtLmhlYWRlcl9yb3c9VCwgbS5zZXA9IiwiLAogICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZXMubmFtZXM9TlVMTCwgemVyb19maWxsaW5nPVQsIGFwb2RpemF0aW9uPVQsIHppcHBlZD1ULAogICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPSIiLCBsYWJlbC54PSJwcG0iLCBsYWJlbC52YWx1ZXM9ImludGVuc2l0eSIpCgogICogX3Zhcmlhblxfc3BlY3RyYVxfZGlyZWN0b3J5Xzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2YgdGhlIGRhdGEgZm9sZGVyIHdpdGggYWxsIHRoZSBzcGVjdHJhIGZvbGRlcnM7CiAgCiAgKiBfbWV0YWRhdGFcX2ZpbGVfOiBzdHJpbmcgY29udGFpbmluZyB0aGUgcGF0aCBvZiB0aGUgbWV0YWRhdGEgZmlsZTsgX19vcHRpb25hbCBidXQgcmVjb21lbmRlZF9fCiAgCiAgKiBfbS5oZWFkZXJcX2NvbF86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgY29sdW1uIHdpdGggdGhlIG5hbWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9tLmhlYWRlclxfcm93XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgZmlsZSBjb250YWlucyBhIGhlYWRlciByb3cgd2l0aCB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9tLnNlcF86IHRoZSBzZXBhcmF0b3IgY2hhcmFjdGVyIG9mIHRoZSBtZXRhZGF0YSBmaWxlLiBEZWZhdWx0cyB0byAiLCI7CiAgCiAgKiBfc2FtcGxlcy5uYW1lc186IENTViBmaWxlIHdoZXJlIHRoZSBmaXJzdCBjb2x1bW4gcmVwcmVzZW50cyB0aGUgc2FtcGxlcyBuYW1lcyBhbmQgaW4gdGhlIHNlY29uZCBjb2x1bW4gdGhlIG5hbWVzIG9mIHRoZSBzcGVjdHJhIGRpcmVjdG9yaWVzIHRvIHdoaWNoIHRoZXkgY29ycmVzcG9uZC4gSWYgTlVMTCwgaXQgd2lsbCBiZSBjb25zaWRlcmVkIHRoYXQgdGhlIGRpcmVjdG9yaWVzIG5hbWVzIGFyZSB0aGUgc2FtcGxlcyBuYW1lcyAoaXQgaGFzIHRvIGJlIHRoZSBzYW1lIG5hbWVzIHRoYXQgYXBwZWFyIGluIHRoZSBtZXRhZGF0YSBmaWxlKTsKICAKICAqIF96ZXJvXF9maWxsaW5nXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIHplcm8tZmlsbGluZyBzaG91bGQgYmUgcGVyZm9ybWVkIG9yIG5vdCB3aGVuIHByb2Nlc3NpbmcgdGhlIGZpZCBzcGVjdHJhLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2Fwb2RpemF0aW9uXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIGFwb2RpemF0aW9uIHNob3VsZCBiZSBwZXJmb3JtZWQgb3Igbm90IHdoZW4gcHJvY2Vzc2luZyB0aGUgZmlkIHNwZWN0cmEuIERlZmF1bHRzIHRvIFRSVUU7CiAgCiAgKiBfemlwcGVkXzogQm9sZWVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgc3BlY3RyYSBkaXJlY3RvcmllcyBhcmUgemlwcGVkICguemlwKSBvciBub3QuIF9fVGhlIGNvbXByZXNzZWQgZmlsZXMgbXVzdCBoYXZlIHRoZSBleHRlbnNpb24gLnppcC4gSWYgbm90LCB5b3Ugd2lsbCBoYXZlIHRvIHVuY29tcHJlc3MgdGhlbSB5b3Vyc2VsZi5fXwogIAogICogX2Rlc2NyaXB0aW9uXzogYSBzaG9ydCB0ZXh0IGRlc2NyaWJpbmcgdGhlIGRhdGFzZXQuIF9fb3B0aW9uYWxfXwogIAogICogX2xhYmVsLnhfOiB0aGUgbGFiZWwgZm9yIHRoZSB4IHZhbHVlcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwudmFsdWVzXzogdGhlIGxhYmVsIGZvciB0aGUgeSB2YWx1ZXMuIF9fb3B0aW9uYWxfXwoKCiMjIyMgQWZ0ZXIgcmVhZGluZyBzcGVjdHJhLCBkZXRlY3Rpb24gYW5kIGFsaWdubWVudCBvZiBwZWFrcyBjYW4gYmUgcGVyZm9ybWVkLCBhcyBpdCBpcyBub3Qgb2JsaWdhdG9yeSB3aGVuIHJlYWRpbmcgdGhlIGRhdGEsIHdoaWNoIGhhcHBlbnMgd2hlbiByZWFkaW5nIE1TIHNwZWN0cmEKCl9fRGV0ZWN0aW9uIG9mIHBlYWtzLCBmb2xsb3dlZCBieSBhbGlnbm1lbnQgb2YgdGhvc2UgcGVha3NfXwoKPiBkZXRlY3Rfbm1yX3BlYWtzX2Zyb21fZGF0YXNldChkYXRhc2V0LCBiYXNlbGluZV90cmVzaD01MDAwMCwgYXAubWV0aG9kPSJvd24iLCBhcC5zYW1wLmNsYXNzZXM9MSwgYXAuc3RlcD0wLjAzKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0IG9mIHR5cGUgX25tci1zcGVjdHJhXzsKICAKICAqIF9iYXNlbGluZVxfdHJlc2hfOiBNaW5pbXVtIGludGVuc2l0eSB2YWx1ZSB0aGF0IHBlYWtzIG11c3QgaGF2ZS4gUGVha3Mgd2l0aCBpbnRlbnNpdHkgc21hbGxlciB0aGFuIGJhc2VsaW5lX3RyZXNoIHdpbGwgbm90IGJlIGNvbnNpZGVyZWQgYXMgZGV0ZWN0ZWQgcGVha3MuIERlZmF1bHRzIHRvIDUwMDAwOwogIAogICogX2FwLm1ldGhvZF86IG1ldGhvZCBvZiBwZWFrIGFsaWdubWVudC4gQ2FuIGVpdGhlciBiZQoKICAgICAgKyAib3duIjogU3BlY21pbmUgbWV0aG9kLiBEZWZhdWx0IHZhbHVlOwoKICAgICAgKyAibWV0YWJvYW5hbHlzdCI6IE1ldGFib0FuYWx5c3QgbWV0aG9kLCB3aGljaCBpcyBmb3IgdXNpbmcgdGhlIHBlYWsgYWxpZ25tZW50IHVzZWQgaW4gTWV0YWJvQW5hbHlzdCBzb2Z0d2FyZS4KCiAgKiBfYXAuc2FtcC5jbGFzc2VzXzogc3RyaW5nIGNvbnRhaW5nIHRoZSBtZXRhZGF0YeKAmXMgdmFyaWFibGUgbmFtZSB0byBiZSB1c2VkIGluIHRoZSBNZXRhYm9BbmFseXN0IG1ldGhvZC4gQ2FuIGJlIG9idGFpbmVkIGZyb20gY29sbmFtZXMobWV0YWRhdGFfZGF0YWZyYW1lKS4gRGVmYXVsdHMgdG8gdGhlIHZhcmlhYmxlIHJlcHJlc2VudGVkIGJ5IHRoZSBmaXJzdCBjb2x1bW4uCiAgCiAgKiBfYXAuc3RlcF86IHN0ZXAgdmFsdWUgZm9yIHRoZSBwZWFrIGFsaWdubWVudCBwcm9jZXNzIGluIHRoZSBzcGVjbWluZSBtZXRob2QuIERlZmF1bHRzIHRvIDAuMDMuCgoKIyMjIEV4YW1wbGVzCgojIyMjIEJSVUtFUiBkYXRhCgpUaGUgZXhhbXBsZSBkYXRhIGhlcmUgdXNlZCB3YXMgb2J0YWluZWQgZnJvbSB0aGUgTWV0YWJvbGlnaHRzIGRhdGFiYXNlLCB1bmRlciB0aGUgSUQgTVRCTFMxNTEuIFRoZSBkYXRhIHdhcyBvYnRhaW5lZCB1c2luZyB0aGUgW19nZXRcX21ldGFib2xpZ2h0c1xfc3R1ZHlfXSgjZ2V0X21ldGFib2xpZ2h0c19zdHVkeSkgZnVuY3Rpb24uCgpfXzEuX18gU3RyaW5ncyBpbmRpY2F0aW5nIHdoZXJlIGRhdGEgYW5kIG1ldGFkYXRhIGlzOgoKYGBge3J9CmJydWtlcl9ubXJfc3BlY3RyYV9mb2xkZXI9Ii9ob21lL3NjYXJkb3NvL0RvY3VtZW50cy9tZXRhYm9sb21pY3NfZGF0YXNldHMvTk1SL1NwZWN0cmEvTVRCTFMxNTEiCmJydWtlcl9ubXJfbWV0YWRhdGFfZmlsZT0iL2hvbWUvc2NhcmRvc28vRG9jdW1lbnRzL21ldGFib2xvbWljc19kYXRhc2V0cy9OTVIvU3BlY3RyYS9NVEJMUzE1MS9tZXRhZGF0YS5jc3YiCmBgYAoKX18yLl9fIExvYWRpbmcgTk1SIGJydWtlciBzcGVjdHJhbCBkYXRhIHRvIHNwZWNtaW5lOgoKX0luIHRoaXMgZGF0YSwgdGhlIHNwZWN0cmEgZm9sZGVycycgbmFtZXMgY29ycmVzcG9uZCB0byB0aGUgbmFtZXMgb2YgdGhlIHNhbXBsZXMsIHNvIG5vIGZpbGUgd2lsbCBiZSBnaXZlbiB0byB0aGUgYXJndW1lbnQgc2FtcGxlcy5uYW1lcy4gVGhlc2UgZm9sZGVycyBhcmUgemlwcGVkICguemlwKV8KCmBgYHtyfQpubXJfYnJ1a2VyX3NwZWN0cmFfZGF0YXNldD1yZWFkX0JydWtlcl9maWxlcyhicnVrZXJfbm1yX3NwZWN0cmFfZm9sZGVyLCBtZXRhZGF0YV9maWxlPWJydWtlcl9ubXJfbWV0YWRhdGFfZmlsZSkKYGBgCgpfXzMuX18gVGFibGUgb2YgdGhlIGRhdGEganVzdCBsb2FkZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShubXJfYnJ1a2VyX3NwZWN0cmFfZGF0YXNldCRkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCl9fNC5fXyBUYWJsZSBvZiB0aGUgbWV0YWRhdGEganVzdCBsb2FkZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShubXJfYnJ1a2VyX3NwZWN0cmFfZGF0YXNldCRtZXRhZGF0YSkKYGBgCgpfXzUuX18gRGV0ZWN0aW9uIGFuZCBhbGlnbm1lbnQgb2YgcGVha3M6CgpIZXJlLCB3ZSB3YW50IHRoZSBkZXRlY3RlZCBwZWFrcyB0byBoYXZlIGEgbWluaW11bSBpbnRlbnNpdHkgb2YgNTAwMDAwMC4gVGhleSB3aWxsIGFsc28gYmUgYWxpZ25lZCBhY2NvcmRpbmcgdG8gdGhlIHNwZWNtaW5lIG1ldGhvZCwgd2l0aCBhIHN0ZXAgdmFsdWUgb2YgMC4wMy4gQnV0IGZpcnN0LCB3ZSBoYXZlIHRvIHRyZWF0IHRoZSBtaXNzaW5nIHZhbHVlcyAoZGV0YWlscyBbYmVsb3ddKCNyZWFkX25tcl9wZWFrc19leCkpLCBvdGhlcndpemUgdGhlIGZ1bmN0aW9uIHRvIGRldGVjdCBhbmQgYWxpZ24gcGVha3Mgd2lsbCBub3Qgd29yay4KCmBgYHtyfQojVHJlYXQgbWlzc2luZyB2YWx1ZXM6Cm5tcl9icnVrZXJfc3BlY3RyYV9kYXRhc2V0X212PW1pc3Npbmd2YWx1ZXNfaW1wdXRhdGlvbihubXJfYnJ1a2VyX3NwZWN0cmFfZGF0YXNldCwgbWV0aG9kID0gInZhbHVlIiwgdmFsdWU9MCkKCiNEZXRlY3QgYW5kIGFsaWduIHBlYWtzLCBub3cgdGhhdCBtaXNzaW5nIHZhbHVlcyB3ZXJlIHRyZWF0ZWQ6Cm5tcl9icnVrZXJfcGVha3NfZGF0YXNldD1kZXRlY3Rfbm1yX3BlYWtzX2Zyb21fZGF0YXNldChubXJfYnJ1a2VyX3NwZWN0cmFfZGF0YXNldF9tdiwgYmFzZWxpbmVfdHJlc2ggPSA1MDAwMDAwKQpgYGAKCl9fNi5fXyBUYWJsZSBvZiB0aGUgZGF0YSB3aXRoIHRoZSBwZWFrcyBkZXRlY3RlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKG5tcl9icnVrZXJfcGVha3NfZGF0YXNldCRkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCgojIyMjIFZBUklBTiBkYXRhCgpUaGUgZXhhbXBsZSBkYXRhIGhlcmUgdXNlZCB3YXMgb2J0YWluZWQgZnJvbSB0aGUgTWV0YWJvbGlnaHRzIGRhdGFiYXNlLCB1bmRlciB0aGUgSUQgTVRCTFMzNDYuIFRoZSBkYXRhIHdhcyBvYnRhaW5lZCB1c2luZyB0aGUgW19nZXRcX21ldGFib2xpZ2h0c1xfc3R1ZHlfXSgjZ2V0X21ldGFib2xpZ2h0c19zdHVkeSkgZnVuY3Rpb24uCgpfVGhlIGZvbGRlciBuYW1lZCAiMTQiIHdhcyBub3QgdXNlZCBpbiB0aGlzIGV4YW1wbGUgKGkuZS4gbm90IHByZXNlbnQgaW4gdGhlIGRhdGEgZm9sZGVyIGhlcmUgcHJvdmlkZWQpLCBhcyBkYXRhIHdhcyB0b28gYmlnIHRvIGJlIHJlYWQuIFRoZXJlZm9yZSwgaXRzIG1ldGFkYXRhIGluZm9ybWF0aW9uIHdhcyBhbHNvIGRlbGV0ZWQgZnJvbSB0aGUgbWV0YWRhdGEgZmlsZS5fCgpfXzEuX18gU3RyaW5ncyBpbmRpY2F0aW5nIHdoZXJlIGRhdGEgYW5kIG1ldGFkYXRhIGlzOgoKYGBge3J9CnZhcmlhbl9ubXJfc3BlY3RyYV9mb2xkZXI9Ii9ob21lL3NjYXJkb3NvL0RvY3VtZW50cy9tZXRhYm9sb21pY3NfZGF0YXNldHMvTk1SL1NwZWN0cmEvTVRCTFMzNDYiCnZhcmlhbl9ubXJfbWV0YWRhdGFfZmlsZT0iL2hvbWUvc2NhcmRvc28vRG9jdW1lbnRzL21ldGFib2xvbWljc19kYXRhc2V0cy9OTVIvU3BlY3RyYS9NVEJMUzM0Ni9tZXRhZGF0YV9ub18xNC5jc3YiCmBgYAoKX18yLl9fIEluIHRoaXMgZGF0YSwgdGhlIHNwZWN0cmEgZm9sZGVycycgbmFtZXMgZG8gbm90IGNvcnJlc3BvbmQgdG8gdGhlIG5hbWVzIG9mIHRoZSBzYW1wbGVzLCBzbyBhIGZpbGUgKGhlcmUgcmVwcmVzZW50ZWQgYnkgdmFyaWFibGUgX3NhbXBsZXNcX2ZvbGRlcnNfKSB3aWxsIGJlIGdpdmVuIHRvIHRoZSBhcmd1bWVudCBzYW1wbGVzLm5hbWVzLgoKCmBgYHtyfQpzYW1wbGVzX2ZvbGRlcnM9Ii9ob21lL3NjYXJkb3NvL0RvY3VtZW50cy9tZXRhYm9sb21pY3NfZGF0YXNldHMvTk1SL1NwZWN0cmEvTVRCTFMzNDYvc2FtcGxlc19maWxlcy5jc3YiCmBgYAoKQ29udGVudCBvZiBfc2FtcGxlc1xfZm9sZGVyc18gZmlsZSwgd2hlcmUgdGhlIGZpcnN0IGNvbHVtbiBjb3JyZXNwb25kcyB0byB0aGUgbmFtZXMgb2YgdGhlIHNhbXBsZXMgYW5kIHRoZSBzZWNvbmQgb25lIHRvIHRoZSBmb2xkZXJzIG5hbWVzIHdoZXJlIHRoZSBjb3JyZXNwb25kaW5nIGRhdGEgaXMgc3RvcmVkOgoKPHAgc3R5bGUgPSAndGV4dC1hbGlnbjogY2VudGVyOyc+CiFbXSgvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMvc3BlY21pbmUvcmVwb3J0cy9zYW1wbGVzX2ZvbGRlcnMucG5nKQo8L3A+CgpfXzIuX18gTG9hZGluZyBOTVIgdmFydGlhbiBzcGVjdHJhbCBkYXRhIHRvIHNwZWNtaW5lOgoKX1RoZSBmb2xkZXJzIGhlcmUgcmVhZCBhcmUgbm90IHppcHBlZCAoLnppcCksIGFzIHRoZXkgcHJldmlvdXNseSB1bmNvbXByZXNzZWQgZm9yIHB1cnBvc2Ugb2Ygc2hvd2luZyBob3cgdG8gcHJvY2VlZCBpbiBzdWNoIGEgY2FzZS4gTm8gemVybyBmaWxsaW5nIHdpbGwgYmUgcGVyZm9ybWVkLCBidXQgYXBvZGl6YXRpb24gd2lsbCBiZV8KCmBgYHtyfQpubXJfdmFyaWFuX3NwZWN0cmFfZGF0YXNldD1yZWFkX3Zhcmlhbl9zcGVjdHJhX3Jhdyh2YXJpYW5fbm1yX3NwZWN0cmFfZm9sZGVyLCBtZXRhZGF0YV9maWxlPXZhcmlhbl9ubXJfbWV0YWRhdGFfZmlsZSwgc2FtcGxlcy5uYW1lcz1zYW1wbGVzX2ZvbGRlcnMsIHppcHBlZD1GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZXJvX2ZpbGxpbmc9RikKYGBgCgpfXzMuX18gVGFibGUgb2YgdGhlIGZpcnN0IDUwMCBkYXRhIHBvaW50cyBvZiB0aGUgZGF0YSBqdXN0IGxvYWRlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKG5tcl92YXJpYW5fc3BlY3RyYV9kYXRhc2V0JGRhdGFbMTo1MDAsXSwgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFKSkKYGBgCgpfXzQuX18gVGFibGUgb2YgdGhlIG1ldGFkYXRhIGp1c3QgbG9hZGVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUobm1yX3Zhcmlhbl9zcGVjdHJhX2RhdGFzZXQkbWV0YWRhdGEpCmBgYAoKX181Ll9fIERldGVjdGlvbiBhbmQgYWxpZ25tZW50IG9mIHBlYWtzOgoKSGVyZSwgd2Ugd2FudCB0aGUgZGV0ZWN0ZWQgcGVha3MgdG8gaGF2ZSBhIG1pbmltdW0gaW50ZW5zaXR5IG9mIDIwMDAwMDAwLiBUaGV5IHdpbGwgYWxzbyBiZSBhbGlnbmVkIGFjY29yZGluZyB0byB0aGUgc3BlY21pbmUgbWV0aG9kLCB3aXRoIGEgc3RlcCB2YWx1ZSBvZiAwLjAzLgoKYGBge3J9CiNEZXRlY3QgYW5kIGFsaWduIHBlYWtzOgpubXJfdmFyaWFuX3BlYWtzX2RhdGFzZXQ9ZGV0ZWN0X25tcl9wZWFrc19mcm9tX2RhdGFzZXQobm1yX3Zhcmlhbl9zcGVjdHJhX2RhdGFzZXQsIGJhc2VsaW5lX3RyZXNoID0gMjAwMDAwMDApCmBgYAoKX182Ll9fIFRhYmxlIG9mIHRoZSBkYXRhIHdpdGggdGhlIHBlYWtzIGRldGVjdGVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUobm1yX3Zhcmlhbl9wZWFrc19kYXRhc2V0JGRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCmBgYAoKCgojIyBHQy9MQy1NUyBTcGVjdHJhIChwZWFrIGRldGVjdGlvbiBjb21lcyB3aXRoIGRhdGEgcmVhZGluZykKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgo8cCBzdHlsZSA9ICd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+Cl9fUmVhZCBNUyBzcGVjdHJhIGRhdGEgYW5kIG1ldGFkYXRhIGludG8gc3BlY21pbmUgZGF0YXNldF9fCgo+IHJlYWRfbXNfc3BlY3RyYShmb2xkZXIubmFtZSwgdHlwZSA9ICJ1bmRlZmluZWQiLGZpbGVuYW1lLm1ldGEgPSBOVUxMLCBkZXNjcmlwdGlvbiA9ICIiLCBwcm9mLm1ldGhvZCA9ICJiaW4iLCBmd2htID0gMzAsIGJ3ID0gMzAsIGludHZhbHVlID0gImludG8iLCBoZWFkZXIuY29sLm1ldGEgPSBUUlVFLCBoZWFkZXIucm93Lm1ldGEgPSBUUlVFLCBzZXAubWV0YSA9ICIsIikgIAoKICAqIF9mb2xkZXIubmFtZV86IHN0cmluZyBjb250YWluaW5nIHRoZSBwYXRoIG9mIHRoZSBkYXRhIGZvbGRlcjsKICAKICAqIF90eXBlXzoJdHlwZSBvZiB0aGUgZGF0YS4gRGVmYXVsdHMgdG8gInVuZGVmaW5lZCIsIGJ1dCBpbiB0aGlzIGNhc2Ugc2hvdWxkIGVpdGhlciBiZSAibGNtcy1zcGVjdHJhIiBvciAiZ2Ntcy1zcGVjdHJhIjsKCiAgKiBfZmlsZW5hbWUubWV0YV86IHN0cmluZyBjb250YWluaW5nIHRoZSBwYXRoIG9mIHRoZSBtZXRhZGF0YSBmaWxlOyBfX29wdGlvbmFsIGJ1dCByZWNvbWVuZGVkX18KICAKICAqIF9kZXNjcmlwdGlvbl86IGEgc2hvcnQgdGV4dCBkZXNjcmliaW5nIHRoZSBkYXRhc2V0LiBfX29wdGlvbmFsX18KICAKICAqIF9wcm9mLm1ldGhvZF86IHByb2ZpbGUgZ2VuZXJhdGlvbiBtZXRob2QuIEVpdGhlciAiYmluIiwgImJpbmxpbiIsICJiaW5saW5iYXNlIiBvciAiaW50bGluIi4gRGVmYXVsdHMgdG8gImJpbiIuIHByb2ZtZXRob2QgcGFyYW1ldGVyIGZyb20geGNtc1NldCBmdW5jdGlvbiBmcm9tIHhjbXMgcGFja2FnZS4KICAKICAqIF9md2htXzogZnVsbCB3aWR0aCBhdCBoYWxmIG1heGltdW0gb2YgbWF0Y2hlZCBmaWx0cmF0aW9uIGdhdXNzaWFuIG1vZGVsIHBlYWsuIENvbW1vbmx5IDMwIGZvciBMQy1NUyBzcGVjdHJhIGFuZCA0IGZvciBHQy1NUyBzcGVjdHJhLiBEZWZhdWx0cyB0byAzMC4gZndobSBwYXJhbWV0ZXIgZnJvbSB4Y21zU2V0IGZ1bmN0aW9uIGZyb20geGNtcyBwYWNrYWdlLgogIAogICogX2J3XzogYmFuZHdpZHRoIChzdGFuZGFyZCBkZXZpYXRpb24gb3IgaGFsZiB3aWR0aCBhdCBoYWxmIG1heGltdW0pIG9mIHRoZSBHYXVzc2lhbiBzbW9vdGhpbmcga2VybmVsLCB0byBhcHBseSB0byB0aGUgcGVhayBkZW5zaXR5IGNocm9tYXRvZ3JhbS4gQ29tbW9ubHkgMzAgZm9yIExDLU1TIHNwZWN0cmEgYW5kIDUgZm9yIEdDLU1TIHNwZWN0cmEuIERlZmF1bHRzIHRvIDMwLiBidyBwYXJhbWV0ZXIgZnJvbSBncm91cCBmdW5jdGlvbiBmcm9tIHhjbXMgcGFja2FnZS4KICAKICAqIF9pbnR2YWx1ZV86IHBlYWsgaW50ZW5zaXR5IG1lYXN1cmUgKGludHZhbHVlIHZhbHVlIHBhcmFtZXRlciBmcm9tIGdyb3VwdmFsIGZ1bmN0aW9uIGZyb20geGNtcyBwYWNrYWdlKS4gSXQgY2FuIGVpdGhlciBiZQogIAogICAgICArICJpbnRvIiAtIGludGVncmF0ZWQgYXJlYSBvZiBvcmlnaW5hbCAocmF3KSBwZWFrLiBEZWZhdWx0IHZhbHVlOwogICAgICAKICAgICAgKyAiaW50ZiIgLSBpbnRlZ3JhdGVkIGFyZWEgb2YgZmlsdGVyZWQgcGVhazsKCiAgICAgICsgIm1heG8iIC0gbWF4aW11bSBpbnRlbnNpdHkgb2Ygb3JpZ2luYWwgKHJhdykgcGVhazsKCiAgICAgICsgIm1heGYiIC0gbWF4aW11bSBpbnRlbnNpdHkgb2YgZmlsdGVyZWQgcGVhazsKICAgICAgICAKICAqIF9oZWFkZXIuY29sLm1ldGFfIDogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgZmlsZSBjb250YWlucyBhIGhlYWRlciBjb2x1bW4gd2l0aCB0aGUgbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGVzLiBEZWZhdWx0cyB0byBUUlVFLgogIAogICogX2hlYWRlci5yb3cubWV0YV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWUgb2YgdGhlIHNhbXBsZXMuIERlZmF1bHRzIHRvIFRSVUUuCiAgCiAgKiBfc2VwLm1ldGFfOiB0aGUgc2VwYXJhdG9yIGNoYXJhY3RlciBvZiB0aGUgbWV0YWRhdGEgZmlsZS4gRGVmYXVsdHMgdG8gIiwiLgo8L3A+CgojIyMgRXhhbXBsZSA8YSBuYW1lPSJyZWFkX21zX3NwZWN0cmFfZXgiPjwvYT4KCl9fMS5fXyBTdHJpbmdzIGluZGljYXRpbmcgd2hlcmUgZGF0YSBhbmQgbWV0YWRhdGEgaXM6CgpgYGB7cn0KbGNtc19kYXRhX2ZvbGRlcj0iL2hvbWUvc2NhcmRvc28vRG9jdW1lbnRzL21ldGFib2xvbWljc19kYXRhc2V0cy9NUy9TcGVjdHJhL21pY2VTcGluYWxDb3JkL2RhdGEiCmxjbXNfbWV0YWRhdGFfZmlsZT0iL2hvbWUvc2NhcmRvc28vRG9jdW1lbnRzL21ldGFib2xvbWljc19kYXRhc2V0cy9NUy9TcGVjdHJhL21pY2VTcGluYWxDb3JkL21ldGFkYXRhX2xjbXMuY3N2IgpgYGAKCl9fMi5fXyBMb2FkaW5nIExDLU1TIGRhdGEgdG8gc3BlY21pbmU6CgpgYGB7cn0KbGNtc19kYXRhc2V0ID0gcmVhZF9tc19zcGVjdHJhKGxjbXNfZGF0YV9mb2xkZXIsIHR5cGUgPSAibGNtcy1zcGVjdHJhIiwgbGNtc19tZXRhZGF0YV9maWxlLCBkZXNjcmlwdGlvbiA9ICJsYy1tcyBtaWNlIHNwaW5hbCBjb3JkIHNhbXBsZXMiKQpgYGAKCl9fMy5fXyBUYWJsZSBvZiB0aGUgZGF0YSBqdXN0IGxvYWRlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKGxjbXNfZGF0YXNldCRkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCl9fNC5fXyBUYWJsZSBvZiB0aGUgbWV0YWRhdGEganVzdCBsb2FkZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShsY21zX2RhdGFzZXQkbWV0YWRhdGEpCmBgYAoKCiMjIFVWLVZpcywgSVIgYW5kIFJhbWFuIFNwZWN0cmEKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgojIyMjIFdoZW4gdGhlIGRhdGEgaXMgaW4gYSBDU1YgZmlsZQoKX19SZWFkIGRhdGEgYW5kIG1ldGFkYXRhX18KCj4gcmVhZFxfZGF0YXNldFxfY3N2KGZpbGVuYW1lLmRhdGEsIGZpbGVuYW1lLm1ldGEgPSBOVUxMLCB0eXBlID0gInVuZGVmaW5lZCIsIGRlc2NyaXB0aW9uID0gIiIsIGxhYmVsLnggPSBOVUxMLCBsYWJlbC52YWx1ZXMgPSBOVUxMLCBzYW1wbGUubmFtZXMgPSBOVUxMLCBmb3JtYXQgPSAicm93IiwgaGVhZGVyLmNvbCA9IFRSVUUsIGhlYWRlci5yb3cgPSBUUlVFLCBzZXAgPSAiLCIsIGhlYWRlci5jb2wubWV0YSA9IFRSVUUsIGhlYWRlci5yb3cubWV0YSA9IFRSVUUsIHNlcC5tZXRhID0gIiwiKQoKICAqIF9maWxlbmFtZS5kYXRhXzoJc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggIG9mIHRoZSBkYXRhIGZpbGU7CiAgCiAgKiBfZmlsZW5hbWUubWV0YV86IHN0cmluZyBjb250YWluaW5nIHRoZSBwYXRoIG1ldGFkYXRhIGZpbGU7IF9fb3B0aW9uYWwgYnV0IHJlY29tZW5kZWRfXwogIAogICogX3R5cGVfOiB0eXBlIG9mIHRoZSBkYXRhLiBEZWZhdWx0cyB0byAidW5kZWZpbmVkIi4gSXQgc2hvdWxkIGJlICJjb25jZW50cmF0aW9ucyI7CiAgCiAgKiBfZGVzY3JpcHRpb25fOiBhIHNob3J0IHRleHQgZGVzY3JpYmluZyB0aGUgZGF0YXNldC4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwueF86IHRoZSBsYWJlbCBmb3IgdGhlIHggdmFsdWVzLiBfX29wdGlvbmFsX18KICAKICAqIF9sYWJlbC52YWx1ZXNfOiB0aGUgbGFiZWwgZm9yIHRoZSB5IHZhbHVlcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfc2FtcGxlLm5hbWVzXzogY2hhcmFjdGVyIHZlY3RvciB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgc2FtcGxlcywgaWYgdGhleSBhcmUgbm90IHByZXNlbnQgaW4gdGhlIGhlYWRlciByb3cgb3IgY29sdW1uOwogIAogICogX2Zvcm1hdF86IGZvcm1hdCB3aGljaCB0aGUgZGF0YSBhcmUgaW4gdGhlIENTViBmaWxlLiBJdCBjYW4gYmUKICAKICAgICAgKyAicm93IjogaWYgdGhlIHNhbXBsZXMgYXJlIGluIHRoZSByb3dzIChlYWNoIGxpbmUgY29ycmVzcG9uZHMgdG8gYSBzYW1wbGUpOwogICAgICAKICAgICAgKyAiY29sIiBpZiB0aGUgc2FtcGxlcyBhcmUgaW4gdGhlIGNvbHVtbnMgKGVhY2ggY29sdW1uIGNvcnJlc3BvbmRzIHRvIGEgc2FtcGxlKS4KICAgICAgCiAgKiBfaGVhZGVyLmNvbF86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIENTViBjb250YWlucyBhIGhlYWRlciBjb2x1bW4gd2l0aCB0aGUgbmFtZXMgb2YgdGhlIHNhbXBsZXMgKGlmIGZvcm1hdD0icm93Iikgb3IgdmFyaWFibGVzIChpZiBmb3JtYXQ9ImNvbCIpLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2hlYWRlci5yb3dfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBDU1YgY29udGFpbnMgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgKGlmIGZvcm1hdD0icm93Iikgb3Igc2FtcGxlcyAoaWYgZm9ybWF0PSJjb2wiKS4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9zZXBfOiB0aGUgc2VwYXJhdG9yIGNoYXJhY3Rlci4gRGVmYXVsdHMgdG8gIiwiOwogIAogICogX2hlYWRlci5jb2wubWV0YV86IGJvb2xlYW4gdmFsdWUgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgQ1NWIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgY29sdW1uIHdpdGggdGhlIG5hbWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9oZWFkZXIucm93Lm1ldGFfOiBib29sZWFuIHZhbHVlIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIENTViBmaWxlIGNvbnRhaW5zIGEgaGVhZGVyIHJvdyB3aXRoIHRoZSBuYW1lIG9mIHRoZSBzYW1wbGVzLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX3NlcC5tZXRhXzogdGhlIHNlcGFyYXRvciBjaGFyYWN0ZXIgb2YgdGhlIG1ldGFkYXRhIGZpbGUuIERlZmF1bHRzIHRvICIsIi4KICAKICAKIyMjIyBXaGVuIHRoZSBkYXRhIGlzIGluIG11bHRpcGxlIENTViBmaWxlcwoKX18xOiBSZWFkIERhdGEgaW50byBsaXN0IG9mIGRhdGEgc2FtcGxlc19fCgo+IHJlYWRcX2NzdnNcX2ZvbGRlcihmb2xkZXJuYW1lLCBoZWFkZXI9VFJVRSwgc2VwPSIsIiwgZGVjPSIuIiwgLi4uKQoKICAqIF9mb2xkZXJuYW1lXzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2YgdGhlIGRhdGEgZm9sZGVyOwogIAogICogX2hlYWRlcl86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciBkYXRhIGZpbGVzIGhhdmUgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWVzIG9mIHRoZSBkYXRhIHZhcmlhYmxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9zZXBfOiB0aGUgc2VwYXJhdG9yIGNoYXJhY3RlciBvZiB0aGUgZGF0YSB2YWx1ZXMuIERlZmF1bHRzIHRvICIsIjsKICAKICAqIF9kZWNfOiBjaGFyYWN0ZXIgdXNlZCBpbiB0aGUgZmlsZSBmb3IgZGVjaW1hbCBwb2ludHMuIERlZmF1bHRzIHRvICIuIjsKICAKICAqIF9za2lwXzogbnVtYmVyIG9mIGxpbmVzIG9mIHRoZSBkYXRhIGZpbGUgdG8gc2tpcCBiZWZvcmUgYmVnaW5uaW5nIHRvIHJlYWQgZGF0YTsgX19vcHRpb25hbF9fCiAgCiAgKiBfLi4uXzogYWRkaXRpb25hbCBwYXJhbWV0ZXJzIGZvciBbcmVhZC5jc3ZdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy91dGlscy92ZXJzaW9ucy8zLjQuMS90b3BpY3MvcmVhZC50YWJsZSkgZnVuY3Rpb24gZnJvbSBbdXRpbHNdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy91dGlscy92ZXJzaW9ucy8zLjQuMSkgcGFja2FnZS4KICAKICAKX18yOiBSZWFkIG1ldGFkYXRhIGZpbGUgKG9wdGlvbmFsIHN0ZXAgYnV0IHJlY29tZW5kZWQpX18KCj4gcmVhZFxfbWV0YWRhdGEoZmlsZW5hbWUsIGhlYWRlci5jb2wgPSBULCBoZWFkZXIucm93ID0gVCwgc2VwID0gIiwiKQoKICAqIF9maWxlbmFtZV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwYXRoIG9mIHRoZSBmaWxlIHdpdGggdGhlIG1ldGFkYXRhOwogIAogICogX2hlYWRlci5jb2xfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBDU1YgZmlsZSBjb250YWlucyBhIGhlYWRlciBjb2x1bW4gd2l0aCB0aGUgbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGVzLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2hlYWRlci5yb3dfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBDU1YgZmlsZSBjb250YWlucyBhIGhlYWRlciByb3cgd2l0aCB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKCiAgKiBfc2VwXzogdGhlIHNlcGFyYXRvciBjaGFyYWN0ZXIuIERlZmF1bHRzIHRvICIsIi4KICAKX18zOiBKb2luIERhdGEgYW5kIE1ldGFkYXRhIChhZGl0aW9uIG9mIG1ldGFkYXRhIGlzLCBhcyBzYWlkLCBvcHRpb25hbCBidXQgcmVjb21lbmRlZCkgaW50byBhIHNwZWNtaW5lIGRhdGFzZXRfXwoKPiBjcmVhdGVfZGF0YXNldChkYXRhbWF0cml4LCB0eXBlID0gInVuZGVmaW5lZCIsIG1ldGFkYXRhID0gTlVMTCwgZGVzY3JpcHRpb24gPSAiIiwgc2FtcGxlLm5hbWVzID0gTlVMTCwgeC5heGlzLnZhbHVlcyA9IE5VTEwsIGxhYmVsLnggPSBOVUxMLCBsYWJlbC52YWx1ZXMgPSBOVUxMKQoKICAqIF9kYXRhbWF0cml4XzogbWF0cml4IHdpdGggbnVtZXJpY2FsIGRhdGE6IHJvd3MgYXJlIGFzc3VtZWQgdG8gYmUgdmFyaWFibGVzIGFuZCBjb2x1bW5zIGFzc3VtZWQgdG8gYmUgc2FtcGxlcy4gQ2FuIGJlIG9iYXRpbmVkIGZyb20gdGhlIGZ1bmN0aW9uIF9yZWFkXF9jc3ZzXF9mb2xkZXJfIGFib3ZlLCBieSB0cmFuc2Zvcm1pbmcgdGhlIGRhdGEgbGlzdCBvYnRhaW5lZCBpbnRvIGEgbnVtZXJpYyBtYXRyaXguIFNlZSBleGFtcGxlcyBmb3IgdGhpczsKICAKICAqIF90eXBlXzogdHlwZSBvZiB0aGUgZGF0YS4gRGVmYXVsdHMgdG8gInVuZGVmaW5lZCIsIGJ1dCBpbiB0aGlzIGNhc2Ugc2hvdWxkIGVpdGhlciBiZSAiaXItc3BlY3RyYSIsICJ1dnYtc3BlY3RyYSIgb3IgInJhbWFuLXNwZWN0cmEiOwogIAogICogX21ldGFkYXRhXzogZGF0YSBmcmFtZSBjb250YWluaW5nIHRoZSBtZXRhZGF0YS4gQ2FuIGJlIG9idGFpbmVkIGZyb20gdGhlIGZ1bmN0aW9uIF9yZWFkXF9tZXRhZGF0YV8gYWJvdmU7IF9fb3B0aW9uYWwgYnV0IHJlY29tZW5kZWRfXwogIAogICogX2Rlc2NyaXB0aW9uXzogc3RyaW5nIHdpdGggYSBzaG9ydCBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YXNldDsgX19vcHRpb25hbF9fCiAgCiAgKiBfc2FtcGxlLm5hbWVzXzogdmVjdG9yIHdpdGggc2FtcGxlIG5hbWVzLiBJZiBOVUxMIHRoZW4gdGhlIGNvbHVtbiBuYW1lcyBvZiBkYXRhbWF0cml4IG9yIHNlcXVlbnRpYWwgbnVtYmVycyB3aWxsIGJlIHVzZWQuIERlZmF1bHRzIHRvIE5VTEw7CiAgCiAgKiBfeC5heGlzLnZhbHVlc186IHZlY3RvciB3aXRoIHRoZSB4IGF4aXMgdmFsdWVzLiBJZiBOVUxMIHRoZW4gdGhlIHJvdyBuYW1lcyBvZiBkYXRhbWF0cml4IG9yIHNlcXVlbnRpYWwgbnVtYmVycyB3aWxsIGJlIHVzZWQuIERlZmF1bHRzIHRvIE5VTEwKICAKICAqIF9sYWJlbC54XzogdGhlIGxhYmVsIGZvciB0aGUgeCB2YWx1ZXMuIF9fb3B0aW9uYWxfXwogIAogICogX2xhYmVsLnlfOiB0aGUgbGFiZWwgZm9yIHRoZSB5IHZhbHVlcy4gX19vcHRpb25hbF9fCgoKCiMjIyMgV2hlbiB0aGUgZGF0YSBpcyBpbiBtdWx0aXBsZSBTUEMgZmlsZXMKCl9fUmVhZCBEYXRhIGFuZCBNZXRhZGF0YV9fCgo+IHJlYWRfZGF0YXNldF9zcGMoZm9sZGVyLmRhdGEsIGZpbGVuYW1lLm1ldGEgPSBOVUxMLCB0eXBlID0gInVuZGVmaW5lZCIsIGRlc2NyaXB0aW9uID0gIiIsIG5vc3ViaGRyID0gRiwgbGFiZWwueCA9IE5VTEwsIGxhYmVsLnZhbHVlcyA9IE5VTEwsIGhlYWRlci5jb2wubWV0YSA9IFRSVUUsIGhlYWRlci5yb3cubWV0YSA9IFRSVUUsIHNlcC5tZXRhID0gIiwiKQoKICAqIF9mb2xkZXIuZGF0YV86IHN0cmluZyBjb250YWluaW5nIHRoZSBwYXRoIG9mIHRoZSBkYXRhIGZvbGRlcjsKICAKICAqIF9maWxlbmFtZS5tZXRhXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIHBhdGggb2YgdGhlIGZpbGUgd2l0aCB0aGUgbWV0YWRhdGE7CiAgCiAgKiBfdHlwZV86IHR5cGUgb2YgdGhlIGRhdGEuIERlZmF1bHRzIHRvICJ1bmRlZmluZWQiLCBidXQgaW4gdGhpcyBjYXNlIHNob3VsZCBlaXRoZXIgYmUgImlyLXNwZWN0cmEiLCAidXZ2LXNwZWN0cmEiIG9yICJyYW1hbi1zcGVjdHJhIjsKICAKICAqIF9kZXNjcmlwdGlvbl86IHN0cmluZyB3aXRoIGEgc2hvcnQgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGFzZXQ7IF9fb3B0aW9uYWxfXwogIAogICogX25vc3ViaGRyXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgc3ViaGVhZGVyIG9mIHRoZSBmaWxlIHNob3VsZCBiZSByZWFkIG9yIG5vdC4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfbGFiZWwueF86ICB0aGUgbGFiZWwgZm9yIHRoZSB4IHZhbHVlcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwudmFsdWVzXzogdGhlIGxhYmVsIGZvciB0aGUgeSB2YWx1ZXMuIF9fb3B0aW9uYWxfXwogIAogICogX2hlYWRlci5jb2wubWV0YV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIENTViBmaWxlIGNvbnRhaW5zIGEgaGVhZGVyIGNvbHVtbiB3aXRoIHRoZSBuYW1lIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZXMuIERlZmF1bHRzIHRvIFRSVUU7CiAgCiAgKiBfaGVhZGVyLnJvdy5tZXRhXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgQ1NWIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWUgb2YgdGhlIHNhbXBsZXMuIERlZmF1bHRzIHRvIFRSVUU7CgogICogX3NlcC5tZXRhXzogdGhlIHNlcGFyYXRvciBjaGFyYWN0ZXIgb2YgdGhlIG1ldGFkYXRhIGZpbGUuIERlZmF1bHRzIHRvICIsIi4KCgoKIyMjIyBXaGVuIHRoZSBkYXRhIGlzIGluIG11bHRpcGxlIChKKURYIGZpbGVzOgoKX19SZWFkIERhdGEgYW5kIE1ldGFkYXRhX18KCj4gcmVhZF9kYXRhc2V0X2R4KGZvbGRlci5kYXRhLCBmaWxlbmFtZS5tZXRhID0gTlVMTCwgdHlwZSA9ICJ1bmRlZmluZWQiLCBkZXNjcmlwdGlvbiA9ICIiLCBsYWJlbC54ID0gTlVMTCwgbGFiZWwudmFsdWVzID0gTlVMTCwgaGVhZGVyLmNvbC5tZXRhID0gVFJVRSwgaGVhZGVyLnJvdy5tZXRhID0gVFJVRSwgc2VwLm1ldGEgPSAiLCIpCgogICogX2ZvbGRlci5kYXRhXzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2YgdGhlIGRhdGEgZm9sZGVyOwogIAogICogX2ZpbGVuYW1lLmRhdGFfOiBzdHJpbmcgY29udGFpbmluZyB0aGUgcGF0aCBvZiB0aGUgZmlsZW5hbWU7IF9fb3B0aW9uYWwgYnV0IHJlY29tZW5kZWRfXwogIAogICogX3R5cGVfOiB0eXBlIG9mIGRhdGEuIERlZmF1bHRzIHRvICJ1bmRlZmluZWQiLCBidXQgaW4gdGhpcyBjYXNlIHNob3VsZCBlaXRoZXIgYmUgImlyLXNwZWN0cmEiLCAidXZ2LXNwZWN0cmEiIG9yICJyYW1hbi1zcGVjdHJhIjsKICAKICAqIF9kZXNjcmlwdGlvbl86IGEgc2hvcnQgdGV4dCBkZXNjcmliaW5nIHRoZSBkYXRhc2V0OyBfX29wdGlvbmFsX18KICAKICAqIF9sYWJlbC54XzogdGhlIGxhYmVsIGZvciB0aGUgeCB2YWx1ZXM7IF9fb3B0aW9uYWxfXwoKICAqIF9sYWJlbC52YWx1ZXNfOiB0aGUgbGFiZWwgZm9yIHRoZSB5IHZhbHVlczsgX19vcHRpb25hbF9fCgogICogX2hlYWRlci5jb2wubWV0YV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIENTViBmaWxlIGNvbnRhaW5zIGEgaGVhZGVyIGNvbHVtbiB3aXRoIHRoZSBuYW1lIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZXMuIERlZmF1bHRzIHRvIFRSVUU7CiAgCiAgKiBfaGVhZGVyLnJvdy5tZXRhXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgQ1NWIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWUgb2YgdGhlIHNhbXBsZXMuIERlZmF1bHRzIHRvIFRSVUU7CgogICogX3NlcC5tZXRhXzogdGhlIHNlcGFyYXRvciBjaGFyYWN0ZXIgb2YgdGhlIG1ldGFkYXRhIGZpbGUuIERlZmF1bHRzIHRvICIsIi4KCgojIyMjIFdoZW4gdGhlIGRhdGEgaXMgaW4gbXVsdGlwbGUgWExTWCBmaWxlczoKClRvIHJlYWQgWExTWCBkYXRhIGZpbGVzIGludG8gc3BlY21pbmUsIHRoZSBmdW5jdGlvbiBfcmVhZC54bHN4XyBtdXN0IGJlIHVzZWQgdG8gcmVhZCBlYWNoIGRhdGEgZmlsZS4gU2VlIGV4YW1wbGUgZm9yIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG92ZXJhbGwgc3RlcHMgdG8gcGVyZm9ybS4KCl9fMTogUmVhZCBEYXRhIChmdW5jdGlvbiBmcm9tIHhsc3ggcGF4a2FnZSlfXwoKT25seSB0aGUgbW9yZSBpbW5wb3J0YW50IGFyZ3VtZW50cyBmb3IgdGhlIGNhc2Ugb2YgbWV0YWJvbG9taWNzIHNwZWN0cmEgd2lsbCBiZSBleHBsYWluZWQgYmVsbG93LiBGb3IgZnVydGhlciBpbmZvcm1hdGlvbiwgZ28gW2hlcmVdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy94bHN4L3ZlcnNpb25zLzAuNS43L3RvcGljcy9yZWFkLnhsc3gpLgoKPiByZWFkLnhsc3goZmlsZSwgc2hlZXRJbmRleCxoZWFkZXI9VFJVRSwgLi4uKQoKICAqIF9maWxlXzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2Ygb25lIG9mIHRoZSBkYXRhIGZpbGVzOwogIAogICogX3NoZWV0SW5kZXhfOiBhIG51bWJlciByZXByZXNlbnRpbmcgdGhlIHNoZWV0IGluZGV4IGluIHRoZSB3b3JrYm9vay4gTm9ybWFsbHkgc2hvdWxkIGJlIDEgKHRoZSBmaXJzdCBzaGVldCBpbiB0aGUgd29ya2Jvb2spOwoKICAqIF9oZWFkZXJfOiBhIGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciB0aGUgZmlyc3Qgcm93IGNvbnRhaW5zIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucy4KICAgIApfXzI6ICBSZWFkIG1ldGFkYXRhIGZpbGUgKG9wdGlvbmFsIHN0ZXAgYnV0IHJlY29tZW5kZWQpX18KCj4gcmVhZFxfbWV0YWRhdGEoZmlsZW5hbWUsIGhlYWRlci5jb2wgPSBULCBoZWFkZXIucm93ID0gVCwgc2VwID0gIiwiKQoKICAqIF9maWxlbmFtZV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwYXRoIG9mIHRoZSBmaWxlIHdpdGggdGhlIG1ldGFkYXRhOwogIAogICogX2hlYWRlci5jb2xfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBDU1YgZmlsZSBjb250YWlucyBhIGhlYWRlciBjb2x1bW4gd2l0aCB0aGUgbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGVzLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2hlYWRlci5yb3dfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBDU1YgZmlsZSBjb250YWlucyBhIGhlYWRlciByb3cgd2l0aCB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9zZXBfOiB0aGUgc2VwYXJhdG9yIGNoYXJhY3Rlci4gRGVmYXVsdHMgdG8gIiwiLgogIApfXzM6IEpvaW4gRGF0YSBhbmQgTWV0YWRhdGEgKGFkaXRpb24gb2YgbWV0YWRhdGEgaXMsIGFzIHNhaWQsIG9wdGlvbmFsIGJ1dCByZWNvbWVuZGVkKSBpbnRvIGEgc3BlY21pbmUgZGF0YXNldF9fCgo+IGNyZWF0ZV9kYXRhc2V0KGRhdGFtYXRyaXgsIHR5cGUgPSAidW5kZWZpbmVkIiwgbWV0YWRhdGEgPSBOVUxMLCBkZXNjcmlwdGlvbiA9ICIiLCBzYW1wbGUubmFtZXMgPSBOVUxMLCB4LmF4aXMudmFsdWVzID0gTlVMTCwgbGFiZWwueCA9IE5VTEwsIGxhYmVsLnZhbHVlcyA9IE5VTEwpCgogICogX2RhdGFtYXRyaXhfOiBtYXRyaXggd2l0aCBudW1lcmljYWwgZGF0YTogcm93cyBhcmUgYXNzdW1lZCB0byBiZSB2YXJpYWJsZXMgYW5kIGNvbHVtbnMgYXNzdW1lZCB0byBiZSBzYW1wbGVzLiBDYW4gYmUgb2JhdGluZWQgZnJvbSB0aGUgZnVuY3Rpb24gX3JlYWRcX2NzdnNcX2ZvbGRlcl8gYWJvdmUsIGJ5IHRyYW5zZm9ybWluZyB0aGUgZGF0YSBsaXN0IG9idGFpbmVkIGludG8gYSBudW1lcmljIG1hdHJpeC4gU2VlIGV4YW1wbGVzIGZvciB0aGlzOwogIAogICogX3R5cGVfOiB0eXBlIG9mIHRoZSBkYXRhLiBEZWZhdWx0cyB0byAidW5kZWZpbmVkIiwgYnV0IGluIHRoaXMgY2FzZSBzaG91bGQgZWl0aGVyIGJlICJpci1zcGVjdHJhIiwgInV2di1zcGVjdHJhIiBvciAicmFtYW4tc3BlY3RyYSI7CiAgCiAgKiBfbWV0YWRhdGFfOiBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIG1ldGFkYXRhLiBDYW4gYmUgb2J0YWluZWQgZnJvbSB0aGUgZnVuY3Rpb24gX3JlYWRcX21ldGFkYXRhXyBhYm92ZTsgX19vcHRpb25hbCBidXQgcmVjb21lbmRlZF9fCiAgCiAgKiBfZGVzY3JpcHRpb25fOiBzdHJpbmcgd2l0aCBhIHNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSBkYXRhc2V0OyBfX29wdGlvbmFsX18KICAKICAqIF9zYW1wbGUubmFtZXNfOiB2ZWN0b3Igd2l0aCBzYW1wbGUgbmFtZXMuIElmIE5VTEwgdGhlbiB0aGUgY29sdW1uIG5hbWVzIG9mIGRhdGFtYXRyaXggb3Igc2VxdWVudGlhbCBudW1iZXJzIHdpbGwgYmUgdXNlZC4gRGVmYXVsdHMgdG8gTlVMTDsKICAKICAqIF94LmF4aXMudmFsdWVzXzogdmVjdG9yIHdpdGggdGhlIHggYXhpcyB2YWx1ZXMuIElmIE5VTEwgdGhlbiB0aGUgcm93IG5hbWVzIG9mIGRhdGFtYXRyaXggb3Igc2VxdWVudGlhbCBudW1iZXJzIHdpbGwgYmUgdXNlZC4gRGVmYXVsdHMgdG8gTlVMTAogIAogICogX2xhYmVsLnhfOiB0aGUgbGFiZWwgZm9yIHRoZSB4IHZhbHVlcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwueV86IHRoZSBsYWJlbCBmb3IgdGhlIHkgdmFsdWVzLiBfX29wdGlvbmFsX18KCgoKIyMjIEV4YW1wbGUocykKCiMjIyMgRGF0YSBpbiBDU1YgRmlsZSAoSVIgU3BlY3RyYSkgPGEgbmFtZT0icmVhZF9pcl9leCI+PC9hPgoKX18xOl9fIFN0cmluZ3MgaW5kaWNhdGluZyB3aGVyZSBkYXRhIGFuZCBtZXRhZGF0YSBpczoKCmBgYHtyfQppcl9zcGVjdHJhX2RhdGFfZmlsZT0iL2hvbWUvc2NhcmRvc28vRG93bmxvYWRzL2Nhc3NhdmFQUEQvZGF0YV9jYXNzYXZhLmNzdiIKaXJfc3BlY3RyYV9tZXRhZGF0YV9maWxlPSIvaG9tZS9zY2FyZG9zby9Eb3dubG9hZHMvY2Fzc2F2YVBQRC9tZXRhZGF0YV9pci5jc3YiCmBgYAoKX18yOl9fIFJlYWQgZGF0YSBhbmQgbWV0YWRhdGEgZmlsZXM6CgpgYGB7cn0KaXJfc3BlY3RyYV9kYXRhc2V0PXJlYWRfZGF0YXNldF9jc3YoaXJfc3BlY3RyYV9kYXRhX2ZpbGUsIGlyX3NwZWN0cmFfbWV0YWRhdGFfZmlsZSwgdHlwZSA9ICJpci1zcGVjdHJhIiwgZm9ybWF0ID0gImNvbCIpCmBgYAoKX18zLl9fIFRhYmxlIG9mIHRoZSBkYXRhIGp1c3QgbG9hZGVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUoaXJfc3BlY3RyYV9kYXRhc2V0JGRhdGFbMToxMDAsXSwgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFKSkKYGBgCgpfXzQuX18gVGFibGUgb2YgdGhlIG1ldGFkYXRhIGp1c3QgbG9hZGVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUoaXJfc3BlY3RyYV9kYXRhc2V0JG1ldGFkYXRhKQpgYGAKCgojIyMjIFhMU1ggRmlsZXMgKFVWLVZpcyBTcGVjdHJhKSAgPGEgbmFtZT0icmVhZF91dnZfZXgiPjwvYT4KCl9fMTpfXyBTdHJpbmdzIGluZGljYXRpbmcgd2hlcmUgZGF0YSBhbmQgbWV0YWRhdGEgaXM6CgpgYGB7cn0KdXZ2aXNfc3BlY3RyYV9kYXRhX2ZvbGRlcj0iL2hvbWUvc2NhcmRvc28vRG93bmxvYWRzL0Nhc3NhdmEgQ2Fyb3Rlbm9pZHMvZGF0YSIKdXZ2aXNfc3BlY3RyYV9tZXRhZGF0YV9maWxlPSIvaG9tZS9zY2FyZG9zby9Eb3dubG9hZHMvQ2Fzc2F2YSBDYXJvdGVub2lkcy9tZXRhZGF0YV91dnZpcy5jc3YiCmBgYAoKX18yOl9fIEZpcnN0LCB3ZSBoYXZlIHRvIGtub3cgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBmaWxlcywgd2hpY2ggYXJlIHRoZSBudW1iZXIgb2YgZGF0YSBwb2ludHMsIGVxdWFsIHRvIGFsbCBYTFNYIGZpbGVzOgoKYGBge3J9CiNMaXN0IG9mIGZpbGUgcGF0aHMgaW4gdGhlIGRhdGEgZm9sZGVyOgpmaWxlcyA9IGxpc3QuZmlsZXModXZ2aXNfc3BlY3RyYV9kYXRhX2ZvbGRlciwgZnVsbC5uYW1lcyA9IFQpCgojTmFtZXMgb2YgdGhlIGZpbGVzIGluIHRoZSBkYXRhIGZvbGRlcjoKZmlsZU5hbWVzID0gbGlzdC5maWxlcyh1dnZpc19zcGVjdHJhX2RhdGFfZm9sZGVyKQoKI1JlYWQgb25lIG9mIHRoZSBmaWxlcyBhbmQgZ2V0IHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgdGhlIHZhbHVlcyBvZiB0aGUgZGF0YSBwb2ludHM6CmZpbGUgPSB4bHN4OjpyZWFkLnhsc3goZmlsZXNbMV0sIHNoZWV0SW5kZXggPSAxLCBoZWFkZXI9RikKbnJvd3MgPSBucm93KGZpbGUpICNudW1iZXIgb2Ygcm93cwpybmFtZXMgPSBmaWxlWywxXSAjVmFsdWVzIG9mIHRoZSBkYXRhIHBvaW50cwpgYGAKCl9fMzpfXyBQcmVwYXJlIGRhdGEgbWF0cml4IHRvIHJlYWQgdG8gdGhlcmUgdGhlIHNwZWN0cmEgZGF0YToKCmBgYHtyfQojRGF0YSBtYXRyaXg6CmRhdGFtYXQgPSBtYXRyaXgobnJvdyA9IG5yb3dzLCBuY29sID0gbGVuZ3RoKGZpbGVzKSkKCiNSb3duYW1lcyBvZiB0aGUgZGF0YSBtYXRyaXggKHZhbHVlcyBvZiBkYXRhIHBvaW50cyk6CnJvd25hbWVzKGRhdGFtYXQpID0gcm5hbWVzCgojQ29sbmFtZXMgb2YgZGF0YSBtYXRyaXggKHNhbXBsZXMgbmFtZXMpLiBIZXJlLCBpdCBpcyBjb25zaWRlcmVkIHRoYXQgdGhlIHNhbXBsZXMgbmFtZXMgYXJlIHRoZSBuYW1lcyBnaXZlbiB0byB0aGUgZmlsZXM6CmV4dCA9IHBhc3RlKCcuJywgdG9vbHM6OmZpbGVfZXh0KGZpbGVOYW1lc1sxXSksIHNlcCA9ICcnKSAjR2V0cyB0aGUgZXh0ZW5zaW9uIG9mIHRoZSBkYXRhIGZpbGVzLCBzbyB0aGF0IGl0IGlzIHRha2VuIGF3YXkgZnJvbSB0aGUgZmlsZSBuYW1lIGFuZCBvbmx5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3RoZSBuYW1lIG9mIHRoZSBmaWxlIGlzIGluc2VydGVkIApjb2xuYW1lcyhkYXRhbWF0KSA9IGdzdWIoZXh0LCAiIiwgZmlsZU5hbWVzKQpgYGAKCl9fNDpfXyBSZWFkIGRhdGEgZmlsZXMgaW50byBkYXRhIG1hdHJpeDoKCmBgYHtyfQpmb3IgKGkgaW4gMTpsZW5ndGgoZmlsZXMpKXsKICB0YWJfZXhjZWwgPSB4bHN4OjpyZWFkLnhsc3goZmlsZXNbaV0sIHNoZWV0SW5kZXggPSAxLCBoZWFkZXIgPSBGKQogIGRhdGFtYXRbLGldID0gYyh0YWJfZXhjZWxbLDJdLCByZXAoTkEsIG5yb3dzLWxlbmd0aCh0YWJfZXhjZWxbLDJdKSkpICNJZiB0aGVyZSdzIG1pc3NpbmcgZGF0YQp9CmBgYAoKX181Ol9fIFJlYWQgbWV0YWRhdGEgZmlsZToKCmBgYHtyfQp1dnZpc19zcGVjdHJhX21ldGFkYXRhPXJlYWRfbWV0YWRhdGEodXZ2aXNfc3BlY3RyYV9tZXRhZGF0YV9maWxlKQpgYGAKCl9fNjpfXyBKb2luIGRhdGEgYW5kIG1ldGFkYXRhIGludG8gdGhlIHNwZWNtaW5lIGRhdGFzZXQ6CgpgYGB7cn0KdXZ2aXNfc3BlY3RyYV9kYXRhc2V0PWNyZWF0ZV9kYXRhc2V0KGRhdGFtYXQsIHV2dmlzX3NwZWN0cmFfbWV0YWRhdGEsIHR5cGUgPSAidXZ2LXNwZWN0cmEiKQpgYGAKCl9fNy5fXyBUYWJsZSBvZiB0aGUgZGF0YSBqdXN0IGxvYWRlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHV2dmlzX3NwZWN0cmFfZGF0YXNldCRkYXRhWzE6MTAwLF0sIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCmBgYAoKX184Ll9fIFRhYmxlIG9mIHRoZSBtZXRhZGF0YSBqdXN0IGxvYWRlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHV2dmlzX3NwZWN0cmFfZGF0YXNldCRtZXRhZGF0YSwgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFKSkKYGBgCgoKCgojIyBDb25jZW50cmF0aW9ucyBEYXRhCgojIyMgRnVuY3Rpb25zIHRvIHVzZQoKX19SZWFkIGRhdGEgYW5kIG1ldGFkYXRhX18KCj4gcmVhZFxfZGF0YXNldFxfY3N2KGZpbGVuYW1lLmRhdGEsIGZpbGVuYW1lLm1ldGEgPSBOVUxMLCB0eXBlID0gInVuZGVmaW5lZCIsIGRlc2NyaXB0aW9uID0gIiIsIGxhYmVsLnggPSBOVUxMLCBsYWJlbC52YWx1ZXMgPSBOVUxMLCBzYW1wbGUubmFtZXMgPSBOVUxMLCBmb3JtYXQgPSAicm93IiwgaGVhZGVyLmNvbCA9IFRSVUUsIGhlYWRlci5yb3cgPSBUUlVFLCBzZXAgPSAiLCIsIGhlYWRlci5jb2wubWV0YSA9IFRSVUUsIGhlYWRlci5yb3cubWV0YSA9IFRSVUUsIHNlcC5tZXRhID0gIiwiKQoKICAqIF9maWxlbmFtZS5kYXRhXzoJc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggIG9mIHRoZSBkYXRhIGZpbGU7CiAgCiAgKiBfZmlsZW5hbWUubWV0YV86IHN0cmluZyBjb250YWluaW5nIHRoZSBwYXRoIG1ldGFkYXRhIGZpbGU7IF9fb3B0aW9uYWwgYnV0IHJlY29tZW5kZWRfXwogIAogICogX3R5cGVfOiB0eXBlIG9mIHRoZSBkYXRhLiBEZWZhdWx0cyB0byAidW5kZWZpbmVkIi4gSXQgc2hvdWxkIGJlICJjb25jZW50cmF0aW9ucyI7CiAgCiAgKiBfZGVzY3JpcHRpb25fOiBhIHNob3J0IHRleHQgZGVzY3JpYmluZyB0aGUgZGF0YXNldC4gX19vcHRpb25hbF9fCiAgCiAgKiBfbGFiZWwueF86IHRoZSBsYWJlbCBmb3IgdGhlIHggdmFsdWVzLiBfX29wdGlvbmFsX18KICAKICAqIF9sYWJlbC52YWx1ZXNfOiB0aGUgbGFiZWwgZm9yIHRoZSB5IHZhbHVlcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfc2FtcGxlLm5hbWVzXzogY2hhcmFjdGVyIHZlY3RvciB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgc2FtcGxlcywgaWYgdGhleSBhcmUgbm90IHByZXNlbnQgaW4gdGhlIGhlYWRlciByb3cgb3IgY29sdW1uOwogIAogICogX2Zvcm1hdF86IGZvcm1hdCB3aGljaCB0aGUgZGF0YSBhcmUgaW4gdGhlIENTViBmaWxlLiBJdCBjYW4gYmUKICAKICAgICAgKyAicm93IjogaWYgdGhlIHNhbXBsZXMgYXJlIGluIHRoZSByb3dzIChlYWNoIGxpbmUgY29ycmVzcG9uZHMgdG8gYSBzYW1wbGUpOwogICAgICAKICAgICAgKyAiY29sIiBpZiB0aGUgc2FtcGxlcyBhcmUgaW4gdGhlIGNvbHVtbnMgKGVhY2ggY29sdW1uIGNvcnJlc3BvbmRzIHRvIGEgc2FtcGxlKS4KICAgICAgCiAgKiBfaGVhZGVyLmNvbF86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIENTViBjb250YWlucyBhIGhlYWRlciBjb2x1bW4gd2l0aCB0aGUgbmFtZXMgb2YgdGhlIHNhbXBsZXMgKGlmIGZvcm1hdD0icm93Iikgb3IgdmFyaWFibGVzIChpZiBmb3JtYXQ9ImNvbCIpLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2hlYWRlci5yb3dfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBDU1YgY29udGFpbnMgYSBoZWFkZXIgcm93IHdpdGggdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgKGlmIGZvcm1hdD0icm93Iikgb3Igc2FtcGxlcyAoaWYgZm9ybWF0PSJjb2wiKS4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9zZXBfOiB0aGUgc2VwYXJhdG9yIGNoYXJhY3Rlci4gRGVmYXVsdHMgdG8gIiwiOwogIAogICogX2hlYWRlci5jb2wubWV0YV86IGJvb2xlYW4gdmFsdWUgaW5kaWNhdGluZyBpZiB0aGUgbWV0YWRhdGEgQ1NWIGZpbGUgY29udGFpbnMgYSBoZWFkZXIgY29sdW1uIHdpdGggdGhlIG5hbWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcy4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9oZWFkZXIucm93Lm1ldGFfOiBib29sZWFuIHZhbHVlIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIENTViBmaWxlIGNvbnRhaW5zIGEgaGVhZGVyIHJvdyB3aXRoIHRoZSBuYW1lIG9mIHRoZSBzYW1wbGVzLiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX3NlcC5tZXRhXzogdGhlIHNlcGFyYXRvciBjaGFyYWN0ZXIgb2YgdGhlIG1ldGFkYXRhIGZpbGUuIERlZmF1bHRzIHRvICIsIi4KCiMjIyBFeGFtcGxlIDxhIG5hbWU9InJlYWRfY29uY2VudHJhdGlvbnNfZXgiPjwvYT4KCl9fMS5fXyBTdHJpbmdzIGluZGljYXRpbmcgd2hlcmUgZGF0YSBhbmQgbWV0YWRhdGEgaXM6CgpgYGB7cn0KY29uY2VudHJhdGlvbnNfZGF0YV9maWxlPSIvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMvbWV0YWJvbG9taWNzX2RhdGFzZXRzL2NvbmNlbnRyYXRpb25zL2NhY2hleGlhL2h1bWFuX2NhY2hleGlhLmNzdiIKY29uY2VudHJhdGlvbnNfbWV0YWRhdGFfZmlsZT0iL2hvbWUvc2NhcmRvc28vRG9jdW1lbnRzL21ldGFib2xvbWljc19kYXRhc2V0cy9jb25jZW50cmF0aW9ucy9jYWNoZXhpYS9tZXRhZGF0YV9jb25jZW50cmF0aW9ucy5jc3YiCmBgYAoKX18yLl9fIFJlYWQgY29uY2VudHJhdGlvbnMgZGF0YSBmaWxlIGFuZCBtZXRhZGF0YSBmaWxlOgoKYGBge3J9CmNvbmNlbnRyYXRpb25zX2RhdGFzZXQ9cmVhZF9kYXRhc2V0X2Nzdihjb25jZW50cmF0aW9uc19kYXRhX2ZpbGUsIGZpbGVuYW1lLm1ldGEgPSBjb25jZW50cmF0aW9uc19tZXRhZGF0YV9maWxlLCB0eXBlID0gImNvbmNlbnRyYXRpb25zIiwgZGVzY3JpcHRpb24gPSAiTWV0YWJvbGl0ZSBjb25jZW50cmF0aW9ucyBmcm9tIHNhbXBsZXMgd2l0aCBvciB3aXRob3V0IGNhY2hleGlhIiwgbGFiZWwueCA9ICJtZXRhYm9saXRlcyIsIGxhYmVsLnZhbHVlcyA9ICJjb25jZW50cmF0aW9uIiwgZm9ybWF0ID0gInJvdyIpCmBgYAoKX18zLl9fIFRhYmxlIG9mIHRoZSBkYXRhIGp1c3QgbG9hZGVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUoY29uY2VudHJhdGlvbnNfZGF0YXNldCRkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCl9fNC5fXyBUYWJsZSBvZiB0aGUgbWV0YWRhdGEganVzdCBsb2FkZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShjb25jZW50cmF0aW9uc19kYXRhc2V0JG1ldGFkYXRhKQpgYGAKCgoKCiMgVXBsb2FkIERhdGEgZnJvbSBNZXRhYm9saWdodHMgRGF0YWJhc2UgPGEgbmFtZT0iZ2V0X21ldGFib2xpZ2h0c19zdHVkeSI+PC9hPgoKIyMgRnVuY3Rpb24gdG8gdXNlCgpfVE8gVVNFIFRISVMgRlVOQ1RJT04gWU9VIE1VU1QgSEFWRSBQWVRIT04zIElOU1RBTExFRCwgV0lUSCBUSEUgTU9EVUxFUyBpc2F0b29scywgb3MsIGZ0cGxpYiwgZ2xvYiwgbG9nZ2luZywgcGFuZGFzLCB0ZW1wZmlsZSwgc2h1dGlsIGFuZCByZSBJTlNUQUxMRURfCgo+IGdldF9tZXRhYm9saWdodHNfc3R1ZHkobXRibHNJRCwgZGlyZWN0b3J5KQoKICAqIF9tdGJsc0lEXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIElEIG9mIHRoZSBtZXRhYm9saWdodHMgc3R1ZHkgdG8gZG93bmxvYWQ7CiAgCiAgKiBfZGlyZWN0b3J5Xzogc3RyaW5nIGluZGljYXRpbmcgdGhlIGRpcmVjdG9yeSB0byB3aGljaCB0aGUgZGF0YSBmaWxlcyBvZiB0aGUgc3R1ZHkgd2lsbCBiZSBkb3dubG9hZGVkLiBUaGUgZGF0YSB3aWxsIGJlIHN0b3JlZCBpbiAiL2RpcmVjdG9yeS9tdGJsc0lEIi4KCiMjIEV4YW1wbGUKCmBgYHtyfQpnZXRfbWV0YWJvbGlnaHRzX3N0dWR5KCJNVEJMUzEzMSIsICIvaG9tZS9zY2FyZG9zby9Eb2N1bWVudHMiKQpgYGAKCiMgRGF0YSBWaXN1YWxpemF0aW9uIAoKIyMgRGF0YXNldCBTdW1tYXJ5CgpUaGlzIGZ1bmN0aW9uIHByb3ZpZGVzIGEgc3VtbWFyeSBvZiB0aGUgbWFpbiBmZWF0dXJlcyBvZiB0aGUgZGF0YXNldDoKCiogRGVzY3JpcHRpb247CgoqIFR5cHIgb2YgZGF0YTsKCiogTnVtYmVyIG9mIHNhbXBsZXM7CgoqIE51bWJlciBvZiBkYXRhcG9pbnRzOwoKKiBOdW1iZXIgb2YgbWV0YWRhdGEgdmFyaWFibGVzLCBpZiB0aGVyZSBpcyBtZXRhZGF0YTsKCiogTGFiZWxzIG9mIHggYXhpcyB2YWx1ZXMgYW5kIGRhdGEgcG9pbnRzLCBpZiBsYWJlbHMgd2VyZSBnaXZlbi4KCiogSWYgdGhlIHVzZXIgd2FudHMgdG8ga25vdyBzb21lIHN0YXRpc3RpY2FsIHByb3BlcnRpZXM6CgogICAgKyBOdW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gZGF0YTsKICAgIAogICAgKyBNZWFuIG9mIGRhdGEgdmFsdWVzOwogICAgCiAgICArIE1lZGlhbiBvZiBkYXRhIHZhbHVlczsKICAgIAogICAgKyBTdGFuZGFyZCBkZXZpYXRpb247CiAgICAKICAgICsgUmFuZ2Ugb2YgdmFsdWVzOwogICAgCiAgICArIFF1YW50aWxlczsKICAgIAojIyMgRnVuY3Rpb24gdG8gdXNlCgo+IHN1bV9kYXRhc2V0KGRhdGFzZXQsIHN0YXRzID0gVCkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9zdGF0c186IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciBzb21lIGdsb2JhbCBzdGF0aXN0aWNzIG9mIHRoZSBkYXRhIHZhbHVlcyBzaG91bGQgYmUgY2FsY3VsYXRlZC4gRGVmYXVsdHMgdG8gVFJVRQogIAojIyMgRXhhbXBsZQoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gTEMtTVMgZGF0YXNldCAoW0xDLU1TIGRhdGFzZXQgdXNlZF0oI3JlYWRfbXNfc3BlY3RyYV9leCkpLgoKX18xLl9fIERhdGEgc3VtbWFyeSB3aXRoIHN0YXRpc3RpY3M6CgpgYGB7cn0Kc3VtX2RhdGFzZXQobGNtc19kYXRhc2V0KQpgYGAKCl9fMi5fXyBEYXRhIHN1bW1hcnkgd2l0aG91dCBzdGF0aXN0aWNzOgoKYGBge3J9CnN1bV9kYXRhc2V0KGxjbXNfZGF0YXNldCwgc3RhdHM9RikKYGBgCgojIyBCb3hwbG90cwoKIyMjIEJveHBsb3Qgb2Ygb25lIG9yIG1vcmUgdmFyaWFibGVzIGRpc3RyaWJ1dGlvbgoKIyMjIyBGdW5jdGlvbiB0byB1c2UKCj4gYm94cGxvdF92YXJpYWJsZXMoZGF0YXNldCwgdmFyaWFibGVzID0gTlVMTCwgc2FtcGxlcyA9IE5VTEwsIGhvcml6b250YWwgPSBULCBjb2wgPSAibGlnaHRibHVlIiwgbmNoYXIubGFiZWwgPSAxMCwgY2V4LmF4aXMgPSAwLjgsIC4uLikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF92YXJpYWJsZXNfOiB2ZWN0b3Igd2l0aCB0aGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyB0byBwbG90LiBJZiBubyBuYW1lcyBhcmUgZ2l2ZW4gKHZhcmlhYmxlcz1OVUxMKSwgYWxsIHZhcmlhYmxlcyBhcmUgcGxvdHRlZC4gRGVmYXVsdHMgdG8gTlVMTDsKCiAgKiBfc2FtcGxlc186IHZlY3RvciB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgc2FtcGxlcyB0byB0YWtlIGludG8gY29uc2lkZXJhdGlvbiB3aGVuIHBsb3R0aW5nIHRoZSB2YXJpYWJsZXMuIElmIG5vIG5hbWVzIGFyZSBnaXZlbiBhbGwgc2FtcGxlcyBhcmUgdGFrZW4gaW50byBjb25zaWRlcmF0aW9uIChzYW1wbGVzPU5VTEwpLiBEZWZhdWx0cyB0byBOVUxMOwogIAogICogX2hvcml6b250YWxfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBib3hwbG90cyBzaG91bGQgYmUgZHJhd24gaG9yaXpvbnRhbHkgb3Igbm90LiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX2NvbF86IHN0cmluZyB0aGF0IHJlcHJlc2VudHMgdGhlIGNvbG9yIG9mIHRoZSBib2RpZXMgb2YgdGhlIGJveHBsb3RzLiBDYW4gZWl0aGVyIGJlICJsaWdodGJsdWUiIChkZWZhdWx0IHZhbHVlKSwgCgogICogX25jaGFyLmxhYmVsXzogbnVtYmVyIG9mIGNoYXJhY3RlcnMgb2YgdmFyaWFibGVzJyBuYW1lcyB0byBkaXNwbGF5LiBJZiBhIHZhcmlhYmxlIGhhcyBtb3JlIHRoYW4gbmNoYXIubGFiZWwgY2hhcmFjdGVycywgb25seSB0aGUgZmlyc3QgbmNoYXIubGFiZWwgY2hhcmFjdGVycyB3aWxsIGJlIGRyYXduLiBEZWZhdWx0cyB0byAxMDsKICAKICAqIF9jZXguYXhpc186IG51bWVyaWMgdmFsdWUgdGhhdCBpbmRpY2F0ZXMgdGhlIGFtb3VudCBieSB3aGljaCB0aGUgYXhpcyBpcyBtYWduaWZpZWQgcmVsYXRpdmUgdG8gdGhlIGRlZmF1bHQuIERlZmF1bHRzIHRvIDAuODsKICAKICAqIF8uLi5fOiBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgb2YgW2JveHBsb3RdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ncmFwaGljcy92ZXJzaW9ucy8zLjQuMy90b3BpY3MvYm94cGxvdCkgZnVuY3Rpb24uCgojIyMjIEV4YW1wbGUKCkV4YW1wbGUgbWFrZXMgdXNlIG9mIGFuIGNvbmNlbnRyYXRpb25zIGRhdGFzZXQgKFtjb25jZW50cmF0aW9ucyBkYXRhc2V0IHVzZWRdKCNyZWFkX2NvbmNlbnRyYXRpb25zX2V4KSkuCgpfXzEuX18gQm94cGxvdCBvZiBhbGwgdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiB0aHJvdWdoIGFsbCBzYW1wbGVzLCB3aXRoIGJveGVzIGRpcG9zZWQgdmVydGljYWxseQoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzfQpib3hwbG90X3ZhcmlhYmxlcyhjb25jZW50cmF0aW9uc19kYXRhc2V0LCBob3Jpem9udGFsPUYsIG5oYXIubGFiZWw9NTAsIGNleC5heGlzPTEpCmBgYAoKX18yLl9fIEJveHBsb3Qgb2YgIlN1Y3Jvc2UiLCAiSGlzdGlkaW5lIiBhbmQgIkFjZXRhdGUiIGRpc3RyaWJ1dGlvbiB0aHJvdWdoIGFsbCBzYW1wbGVzLCB3aXRoIGEgdGl0bGUKCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xM30KYm94cGxvdF92YXJpYWJsZXMoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgdmFyaWFibGVzPWMoIlN1Y3Jvc2UiLCAiSGlzdGlkaW5lIiwgIkFjZXRhdGUiKSwgaG9yaXpvbnRhbD1GLCBuaGFyLmxhYmVsPTUwLCBjZXguYXhpcz0xLjUsIGNleC5tYWluPTEuNSwgbWFpbj0iU3Vjcm9zZSwgSGlzdGlkaW5lIGFuZCBBY2V0YXRlIGRpc3RyaWJ1dGlvbiBvbiB0aGUgZGF0YXNldCIpCmBgYAoKCgojIyMgQm94cGxvdCBvZiBhIHZhcmlhYmxlIGRpc3RyaWJ1dGlvbiBvdmVyIHR3byBtZXRhZGF0YSB2YXJpYWJsZXMKClRoaXMgZnVuY3Rpb24gY2FuIG9ubHkgYmUgdXNlZCBmb3IgZGF0YXNldHMgdGhhdCBoYXZlIG1vcmUgdGhhbiBvbmUgbWV0YWRhdGEgdmFyaWFibGUuCgojIyMjIEZ1bmN0aW9uIHRvIHVzZQoKPiBwbG90dmFyX3R3b2ZhY3RvcihkYXRhc2V0LCB2YXJpYWJsZSwgbWV0YS52YXIxLCBtZXRhLnZhcjIsIGNvbG91ciA9ICJkYXJrYmx1ZSIsIHRpdGxlID0gIiIsIHhsYWJlbCA9IE5VTEwsIHlsYWJlbCA9IE5VTEwpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfdmFyaWFibGVfOiBuYW1lIG9mIHRoZSB2YXJpYWJsZSB0byBwbG90OwogIAogICogX21ldGEudmFyMV86IHRoZSBuYW1lIG9mIG9uZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGVzIHRvIHBsb3Q7CiAgCiAgKiBfbWV0YS52YXIyXzogdGhlIG5hbWUgb2YgYSBkaWZmZXJlbnQgbWV0YWRhdGEgdmFyaWFibGUgdG8gcGxvdCBmcm9tIHRoZSBmaXJzdCBvbmU7CiAgCiAgKiBfY29sb3VyXzogQ29sb3VyIG9mIHRoZSBib3hlcy4gRGVmYXVsdHMgdG8gImRhcmtibHVlIjsKICAKICAqIF90aXRsZV86IHRpdGxlIG9mIHRoZSBwbG90OyBfX29wdGlvbmFsX18KICAKICAqIF94bGFiZWxfOiBsYWJlbCBvZiB0aGUgeHggYXhpczsgX19vcHRpb25hbF9fCiAgCiAgKiBfeWxhYmVsXzogbGFiZWwgb2YgdGhlIHl5IGF4aXM7IF9fb3B0aW9uYWxfXwoKIyMjIyBFeGFtcGxlCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBubXItcGVha3MgZGF0YXNldCAoW05NUiBwZWFrcyBkYXRhc2V0IHVzZWRdKCNyZWFkX25tcl9wZWFrc19leCkpLgoKX18xLl9fIFBsb3QgdGhlIGludGVuc2l0eSBkaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxlIDAuMTYgcHBtIG92ZXIgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcyAic2Vhc29ucyIgYW5kICJhZ3JvcmVnaW9ucyIKCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xMH0KcGxvdHZhcl90d29mYWN0b3Iobm1yX3BlYWtzX2RhdGFzZXQsICIwLjE2IiwgInNlYXNvbnMiLCAiYWdyb3JlZ2lvbnMiLCBjb2xvdXIgPSAiY3lhbiIpCmBgYAoKSW4gdGhpcyBjYXNlLCBhcyB3YXJuZWQsIHNvbWUgc2FtcGxlcyBoYWQgdG8gYmUgZXhjbHVkZWQgdG8gcHJvZHVjZSB0aGUgcGxvdCwgYXMgdGhlcmUgd2VyZSBtaXNzaW5nIHZhbHVlcy4KCgoKIyMjIEJveHBsb3Qgb2Ygb25lIG9yIG1vcmUgdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvdmVyIG9uZSBtZXRhZGF0YSB2YXJpYWJsZQoKIyMjIyBGdW5jdGlvbiB0byB1c2UKCj4gYm94cGxvdF92YXJzX2ZhY3RvcihkYXRhc2V0LCBtZXRhLnZhciwgdmFyaWFibGVzID0gTlVMTCwgc2FtcGxlcyA9IE5VTEwsIGhvcml6b250YWwgPSBGLCBuY2hhci5sYWJlbCA9IDEwLCBjb2wgPSBOVUxMLCB2ZWMucGFyID0gTlVMTCwgY2V4LmF4aXMgPSAwLjgsIHlsYWJzID0gTlVMTCwgLi4uKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGEudmFyXzogbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgdG8gcGxvdDsKICAKICAqIF92YXJpYWJsZXNfOiBuYW1lcyBvZiB0aGUgdmFyaWFibGVzIHRvIHBsb3Q7CiAgCiAgKiBfc2FtcGxlc186IHZlY3RvciB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgc2FtcGxlcyB0byB0YWtlIGludG8gY29uc2lkZXJhdGlvbiB3aGVuIHBsb3R0aW5nIHRoZSB2YXJpYWJsZXMuIElmIG5vIG5hbWVzIGFyZSBnaXZlbiBhbGwgc2FtcGxlcyBhcmUgdGFrZW4gaW50byBjb25zaWRlcmF0aW9uIChzYW1wbGVzPU5VTEwpLiBEZWZhdWx0cyB0byBOVUxMOwogIAogICogX2hvcml6b250YWxfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBib3hwbG90cyBzaG91bGQgYmUgZHJhd24gaG9yaXpvbnRhbHkgb3Igbm90LiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX25jaGFyLmxhYmVsXzogbnVtYmVyIG9mIGNoYXJhY3RlcnMgb2YgdmFyaWFibGVzJyBuYW1lcyB0byBkaXNwbGF5LiBJZiBhIHZhcmlhYmxlIGhhcyBtb3JlIHRoYW4gbmNoYXIubGFiZWwgY2hhcmFjdGVycywgb25seSB0aGUgZmlyc3QgbmNoYXIubGFiZWwgY2hhcmFjdGVycyB3aWxsIGJlIGRyYXduLiBEZWZhdWx0cyB0byAxMDsKICAKICAqIF9jb2xfOiB2ZWN0b3Igd2l0aCB0aGUgZGlmZmVyZW50IGNvbG9ycyBmb3IgZWFjaCBib3ggKGJveGVzIG9mIHRoZSBzYW1lIGNsYXNzIG9mIG1ldGFkYXRhIHZhcmlhYmxlIHdpbGwgaGF2ZSB0aGUgc2FtZSBjb2xvciAtIF9leGFtcGxlIDIuXykuIElmIG5vdCBwcm92aWRlZCwgZGVmYXVsdCBjb2xvcnMgd2lsbCBiZSB1c2VkLCBiZWluZyBpbiB0aGlzIGNhc2Ugb25lIGRpZmZlcmVudCBjb2xvciBmb3IgYWxsIGJveGVzIG9mIGEgZGF0YSB2YXJpYWJsZSAoX2V4YW1wbGUgMS5fKS4gRGVmYXVsdHMgdG8gTlVMTDsKICAKICAqIF92ZWMucGFyXzogaWYgbW9yZSB0aGFuIG9uZSB2YXJpYWJsZSBpcyBjaG9zZW4sIGEgdmVjdG9yIHNwZWNpZnlpbmcgdGhlIGRpc3Bvc2l0aW9uIG9mIHRoZSBwbG90cyBjb3VsZCBiZSB1c2VmdWwuIFRoaXMgdmVjdG9yIHNob3VsZCBoYXZlIHR3byBlbGVtZW50cyAgLSBjKG51bWJlcl9yb3dzLCBudW1iZXJfY29sdW1ucyk7CiAgCiAgKiBfY2V4LmF4aXNfOiBudW1lcmljIHZhbHVlIHRoYXQgaW5kaWNhdGVzIHRoZSBhbW91bnQgYnkgd2hpY2ggdGhlIGF4aXMgaXMgbWFnbmlmaWVkIHJlbGF0aXZlIHRvIHRoZSBkZWZhdWx0LiBEZWZhdWx0cyB0byAwLjg7CiAgCiAgKiBfeWxhYnNfOiB5eSBheGlzIGxhYmVsOyBfX29wdGlvbmFsX18KICAKICAgICogXy4uLl86IGFkZGl0aW9uYWwgcGFyYW1ldGVycyBvZiBbYm94cGxvdF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dyYXBoaWNzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9ib3hwbG90KSBmdW5jdGlvbi4KCiMjIyMgRXhhbXBsZQoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMS5fXyBCb3hwbG90IG9mIHRocmVlIGRhdGEgdmFyaWFibGVzICgiQ3JlYXRpbmUiLCAiU2VyaW5lIiBhbmQgIkxhY3RhdGUiKSBvdmVyIG9uZSBtZXRhZGF0YSB2YXJpYWJsZSAoIk11c2NsZS5sb3NzIikuIFRoZSBwbG90cyBvZiB0aGUgdHdvIGZpcnN0IHZhcmlhYmxlcyB3aWxsIGJlIHBsb3R0ZWQgaW4gdGhlIGZpcnN0IHJvdywgc2lkZSBieSBzaWRlLCBhbmQgdGhlIG90aGVyIG9uZSBpbiB0aGUgc2Vjb25kIHJvdy4gQm94ZXMgaGVyZSBhcmUgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIGRhdGEgdmFyaWFibGUgdGhleSByZXByZXNlbnQuCgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTN9CmJveHBsb3RfdmFyc19mYWN0b3IoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgIk11c2NsZS5sb3NzIiwgdmFyaWFibGVzID0gYygiQ3JlYXRpbmUiLCJTZXJpbmUiLCAiTGFjdGF0ZSIpLCBjZXguYXhpcz0yLCBjZXgubWFpbj0yLCB2ZWMucGFyPWMoMiwyKSkKYGBgCgpfXzIuX18gQm94cGxvdCBvZiB0aHJlZSBkYXRhIHZhcmlhYmxlcyAoIkNyZWF0aW5lIiwgIlNlcmluZSIgYW5kICJMYWN0YXRlIikgb3ZlciBvbmUgbWV0YWRhdGEgdmFyaWFibGUgKCJNdXNjbGUubG9zcyIpLiBUaGUgcGxvdHMgb2YgdGhlIHR3byBmaXJzdCB2YXJpYWJsZXMgd2lsbCBiZSBwbG90dGVkIGluIHRoZSBmaXJzdCByb3csIHNpZGUgYnkgc2lkZSwgYW5kIHRoZSBvdGhlciBvbmUgaW4gdGhlIHNlY29uZCByb3cuQm94ZXMgaGVyZSBhcmUgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIG1ldGFkYXRhIGNsYXNzIHRoZXkgcmVwcmVzZW50LgoKSGVyZSwgY29sb3JzIGFyZSBzZXQgYXV0b21hdGljYWx5OgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzfQpib3hwbG90X3ZhcnNfZmFjdG9yKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsICJNdXNjbGUubG9zcyIsIHZhcmlhYmxlcyA9IGMoIkNyZWF0aW5lIiwiU2VyaW5lIiwgIkxhY3RhdGUiKSwgY2V4LmF4aXM9MS41LCBjZXgubWFpbj0yLCB2ZWMucGFyPWMoMiwyKSwgY29sPTE6bGVuZ3RoKGxldmVscyhnZXRfbWV0YWRhdGEoY29uY2VudHJhdGlvbnNfZGF0YXNldClbLCJNdXNjbGUubG9zcyJdKSkpCmBgYAoKCkFuZCBoZXJlLCB0d28gc3BlY2lmaWMgY29sb3JzIGFyZSBjaG9zZW4gKGFzIHRoZXJlIGFyZSBvbmx5IHR3byBjbGFzc2VzIG9uIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBjaG9zZW4pLiBJZiB5b3UgZG9uJ3Qga25vdyBob3cgbWFueSBjbGFzc2VzIHRoZXJlIGFyZSwgeW91IGNhbiB0eXBlIHNpbWlsYXJseSB0byB0aGUgZm9sbG93aW5nOiBsZW5ndGgobGV2ZWxzKGdldF9tZXRhZGF0YShjb25jZW50cmF0aW9uc19kYXRhc2V0KVssIk11c2NsZS5sb3NzIl0pKS4KCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xM30KYm94cGxvdF92YXJzX2ZhY3Rvcihjb25jZW50cmF0aW9uc19kYXRhc2V0LCAiTXVzY2xlLmxvc3MiLCB2YXJpYWJsZXMgPSBjKCJDcmVhdGluZSIsIlNlcmluZSIsICJMYWN0YXRlIiksIGNleC5heGlzPTEuNSwgY2V4Lm1haW49MiwgdmVjLnBhcj1jKDIsMiksIGNvbD1jKCJncmVlbiIsICJyZWQiKSkKYGBgCgojIyBTcGVjdHJhIHBsb3QKCk9ubHkgbWFrZXMgc2Vuc2UgZm9yIGRhdGEgb2Ygc3BlY3RyYWwgdHlwZS4KCiMjIyBGdW5jdGlvbiB0byB1c2UKCj4gcGxvdF9zcGVjdHJhKGRhdGFzZXQsIGNvbHVtbi5jbGFzcywgZnVuYyA9IE5VTEwsIHNhbXBsZXMgPSBOVUxMLCAgdmFyaWFibGUuYm91bmRzID0gTlVMTCwgeGxhYiA9IE5VTEwsIHlsYWIgPSBOVUxMLCBsdHkgPSAxLCBsZWdlbmQucGxhY2UgPSAidG9wcmlnaHQiLCBjZXggPSAwLjgsIHJldmVyc2UueCA9IEYsIC4uLikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9tZXRhLnZhcl86IG5hbWUgb2YgYSBtZXRhZGF0YSB2YXJpYWJsZSBieSB3aGljaCBlYWNoIHNwZWN0cnVtIHNhbXBsZSB3aWxsIGJlIGNvbG9yZWQ7CiAgCiAgKiBfZnVuXzogZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIHRvIGFwcGx5IHRvIHRoZSBkYXRhLiBEZWZhdWx0cyB0byBOVUxMOyBfX29wdGlvbmFsX18KICAKICAqIF9zYW1wbGVzXzogdmVjdG9yIHdpdGggdGhlIG5hbWVzIG9mIHRoZSBzYW1wbGVzIHRvIHBsb3QuIElmIG5vIG5hbWVzIGFyZSBnaXZlbiBhbGwgc2FtcGxlcyBhcmUgcGxvdHRlZCAoc2FtcGxlcz1OVUxMKS4gRGVmYXVsdHMgdG8gTlVMTDsKICAKICAqIF92YXJpYWJsZS5ib3VuZHNfOiBudW1lcmljIHZlY3RvciB3aXRoIHR3byBlbGVtZW50cyBpbmRpY2F0aW5nIHRoZSBpbnRlcnZhbCBvZiB4LXZhbHVlcyB0byBwbG90OyBfX29wdGlvbmFsX18KICAKICAqIF94bGFiXzogeHggYXhpcyBsYWJlbDsgX19vcHRpb25hbF9fCiAgCiAgKiBfeWxhYl86IHl5IGF4aXMgbGFiZWw7IF9fb3B0aW9uYWxfXwogIAogICogX2xlZ2VuZC5wbGFjZV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwbGFjZSB3aGVyZSB0aGUgbGVnZW5kJ3MgYm94IHdpbGwgYmUgcGxhY2VkLiBDYW4gZWl0aGVyIGJlOiAgImJvdHRvbXJpZ2h0IiwgImJvdHRvbSIsICJib3R0b21sZWZ0IiwgImxlZnQiLCAidG9wbGVmdCIsICJ0b3AiLCAidG9wcmlnaHQiLCAicmlnaHQiIG9yICJjZW50ZXIiOwogIAogICogX2NleF86IG51bWVyaWMgdmFsdWUgaW5kaWNhdGluZyB0aGUgcmVsYXRpdmUgc2l6ZSBvZiB0aGUgbGVnZW5kLiBEZWZhdWx0cyB0byAwLjg7CiAgCiAgKiBfcmV2ZXJzZS54XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgbmRpY2F0aW5nIGlmIHRoZSB4LWF4aXMgd2lsbCBiZSBzaG93biByZXZlcnNlZCAoZGVjcmVhc2luZ2x5KSBvciBub3QuIERlZmF1bHRzIHRvIEZBTFNFOwogIAogICogX2x0eV86IGxpbmUgdHlwZSAocGFyYW1ldGVyIG9mIFttYXRwbG90XShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZ3JhcGhpY3MvdmVyc2lvbnMvMy40LjMvdG9waWNzL21hdHBsb3QpIGZ1bmN0aW9uKTsKICAKICAqIF8uLi5fOiBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgb2YgW21hdHBsb3RdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ncmFwaGljcy92ZXJzaW9ucy8zLjQuMy90b3BpY3MvbWF0cGxvdCkgZnVuY3Rpb24uCgojIyMgRXhhbXBsZQoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gVVYtVmlzIGRhdGFzZXQgKFtVVi1WaXMgZGF0YXNldCB1c2VkXSgjcmVhZF91dnZfZXgpKS4KCl9fMTpfXyBTcGVjdHJhIHBsb3Qgb2YgYWxsIHNhbXBsZXMsIGNvbG91cmVkIGJ5IGEgbWV0YWRhdGEgdmFyaWFibGUgKGhlcmUgaXMgInZhcmlldGllcyIpOgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzfQpwbG90X3NwZWN0cmEodXZ2aXNfc3BlY3RyYV9kYXRhc2V0LCAidmFyaWV0aWVzIiwgY2V4PTEpCmBgYAoKX18yOl9fIFNwZWN0cmEgcGxvdCBvZiBhbGwgc2FtcGxlcywgY29sb3VyZWQgYnkgYSBtZXRhZGF0YSB2YXJpYWJsZSAoaGVyZSBpcyAiY29sb3JzIiksIGJldHdlZW4gdGhlIHh4IHZhbHVlcyA0MDAgYW5kIDYwMCwgZ2l2aW5nIGxhYmVscyB0byB0aGUgYXhpcyBhbmQgYSB0aXRsZToKCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xM30KcGxvdF9zcGVjdHJhKHV2dmlzX3NwZWN0cmFfZGF0YXNldCwgImNvbG9ycyIsIHZhcmlhYmxlLmJvdW5kcz1jKDQwMCw2MDApLCB4bGFiPSJ3YXZlbGVuZ3RoIiwgeWxhYj0iQWJzb3JiYW5jZSIsIG1haW49IlNwZWN0cmEgcGxvdCIsIGNleD0xLjIpCmBgYAoKX18zOl9fIFNwZWN0cmEgcGxvdCBvZiBmb3VyIHNhbXBsZXMgKG9uZSBmcm9tIGVhY2ggY29sb3IpLCBjb2xvdXJlZCBieSB0aGUgbWV0YWRhdGEgdmFyaWFibGUgImNvbG9ycyIsIHdpdGggdGhlIHh4IGF4aXMgdmFsdWVzIGRlY3JlYXNpbmc6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTN9CnBsb3Rfc3BlY3RyYSh1dnZpc19zcGVjdHJhX2RhdGFzZXQsICJjb2xvcnMiLCBzYW1wbGVzPWMoIjMuMSIsICI1LjEiLCAiNy4xIiwgIjExLjEiKSwgcmV2ZXJzZS54PVQsIGNleD0xLjIpCmBgYAoKCiMjIFBlYWtzIFBsb3QKCiMjIyBGdW5jdGlvbiB0byB1c2UKCj4gcGxvdF9wZWFrcyhkYXRhc2V0LCBjb2x1bW4uY2xhc3MsIGZ1bmMgPSBOVUxMLCBzYW1wbGVzID0gTlVMTCwgIHZhcmlhYmxlLmJvdW5kcyA9IE5VTEwsIHhsYWIgPSBOVUxMLCB5bGFiID0gTlVMTCwgbGVnZW5kLnBsYWNlID0gInRvcHJpZ2h0IiwgY2V4ID0gMC44LCByZXZlcnNlLnggPSBGLCBwLnNpemU9MC41LCAuLi4pCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0YS52YXJfOiBuYW1lIG9mIGEgbWV0YWRhdGEgdmFyaWFibGUgYnkgd2hpY2ggZWFjaCBzcGVjdHJ1bSBzYW1wbGUgd2lsbCBiZSBjb2xvcmVkOwogIAogICogX3NhbXBsZXNfOiB2ZWN0b3Igd2l0aCB0aGUgbmFtZXMgb2YgdGhlIHNhbXBsZXMgdG8gcGxvdC4gSWYgbm8gbmFtZXMgYXJlIGdpdmVuIGFsbCBzYW1wbGVzIGFyZSBwbG90dGVkIChzYW1wbGVzPU5VTEwpLiBEZWZhdWx0cyB0byBOVUxMOwogIAogICogX3ZhcmlhYmxlLmJvdW5kc186IG51bWVyaWMgdmVjdG9yIHdpdGggdHdvIGVsZW1lbnRzIGluZGljYXRpbmcgdGhlIGludGVydmFsIG9mIHgtdmFsdWVzIHRvIHBsb3Q7IF9fb3B0aW9uYWxfXwogIAogICogX3hsYWJfOiB4eCBheGlzIGxhYmVsOyBfX29wdGlvbmFsX18KICAKICAqIF95bGFiXzogeXkgYXhpcyBsYWJlbDsgX19vcHRpb25hbF9fCiAgCiAgKiBfbGVnZW5kLnBsYWNlXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIHBsYWNlIHdoZXJlIHRoZSBsZWdlbmQncyBib3ggd2lsbCBiZSBwbGFjZWQuIENhbiBlaXRoZXIgYmU6ICAiYm90dG9tcmlnaHQiLCAiYm90dG9tIiwgImJvdHRvbWxlZnQiLCAibGVmdCIsICJ0b3BsZWZ0IiwgInRvcCIsICJ0b3ByaWdodCIsICJyaWdodCIgb3IgImNlbnRlciI7CiAgCiAgKiBfY2V4XzogbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIHRoZSByZWxhdGl2ZSBzaXplIG9mIHRoZSBsZWdlbmQuIERlZmF1bHRzIHRvIDAuODsKICAKICAqIF9yZXZlcnNlLnhfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBuZGljYXRpbmcgaWYgdGhlIHgtYXhpcyB3aWxsIGJlIHNob3duIHJldmVyc2VkIChkZWNyZWFzaW5nbHkpIG9yIG5vdC4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfcC5zaXplXzogbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIHRoZSByZWxhdGl2ZSBzaXplIG9mIHRoZSBwbG90IHBvaW50cy4gRGVmYXVsdHMgdG8gMC41OwogIAogICogXy4uLl86IGFkZGl0aW9uYWwgcGFyYW1ldGVycyBvZiBbbWF0cGxvdF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dyYXBoaWNzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9tYXRwbG90KSBmdW5jdGlvbi4KCgojIyBTYW1wbGVzJyBTdGF0aXN0aWNzCgojIyMgRnVuY3Rpb24gdG8gdXNlCgo+IHN0YXRzX2J5X3NhbXBsZShkYXRhc2V0LCBzYW1wbGVzID0gTlVMTCkKCiAgKiBfZGF0YXNldF86IHMgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9zYW1wbGVzXzogdmVjdG9yIHdpdGggdGhlIG5hbWVzIG9mIHRoZSBzYW1wbGVzIHRvIHRha2UgaW50byBjb25zaWRlcmF0aW9uIHdoZW4gY2FsY3VsYXRpbmcgdGhlIHN0YXRpc3RpY3MuIElmIG5vIG5hbWVzIGFyZSBnaXZlbiBhbGwgc2FtcGxlcyBhcmUgdXNlZCAoc2FtcGxlcz1OVUxMKS4gRGVmYXVsdHMgdG8gTlVMTC4KCgojIyMgRXhhbXBsZQoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMTpfXyBTdGF0aXN0aWNzIG9uIGFsbCBzYW1wbGVzOgoKYGBge3J9CnNhbXBzLnN0YXRzPXN0YXRzX2J5X3NhbXBsZShjb25jZW50cmF0aW9uc19kYXRhc2V0KQpEVDo6ZGF0YXRhYmxlKHNhbXBzLnN0YXRzLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWD1UUlVFKSkKYGBgCgoKICAKIyMgVmFyaWFibGUncyBTdGF0aXN0aWNzCgojIyMgRnVuY3Rpb24gdG8gdXNlCgo+IHN0YXRzX2J5X3ZhcmlhYmxlKGRhdGFzZXQsIHZhcmlhYmxlcyA9IE5VTEwsICB2YXJpYWJsZS5ib3VuZHMgPSBOVUxMKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3ZhcmlhYmxlc186IHZlY3RvciB3aXRoIHRoZSB2YXJpYWJsZXMgdG8gdXNlIHRvIGNhbGN1bGF0ZSB0aGUgc3RhdGlzdGljcy4gSWYgbnVtZXJpYyB2ZWN0b3IsIGl0IGlzIGFzc3VtZWQgdGhhdCB0aGV5IHJlcHJlc2VudCBpbmRleGVzLiBJZiBubyB2YXJpYWJsZXMgYXJlIGdpdmVuLCBhbGwgYXJlIHVzZWQgKHZhcmlhYmxlcz1OVUxMKS4gRGVmYXVsdHMgdG8gTlVMTDsKICAKICAqIF92YXJpYWJsZS5ib3VuZHNfOiBpZiB0aGUgdmFyaWFibGVzIGFyZSBudW1lcmljLCB5b3UgY2FuIGNob29zZSB0byBzZWxlY3QgdGhlIGludGVydmFsIG9mIHZhcmlhYmxlcyB0byB0YWtlIGludG8gY29uc2lkZXJhdGlvbiwgc28gdGhhdCB5b3UgZG8gbm90IG5lZWQgdG8gc2VsZWN0IGV2ZXJ5IHNpbmdsZSBkYXRhIHBvaW50IHdhbnRlZC4gQWdhaW4sIGlmIG5vIHZhcmlhYmxlcyBhcmUgZ2l2ZW4sIGFsbCBhcmUgdXNlZCAodmFyaWFibGVzPU5VTEwpLiBEZWZhdWx0cyB0byBOVUxMLgoKIyMjIEV4YW1wbGUKCkV4YW1wbGUgbWFrZXMgdXNlIG9mIGFuIGNvbmNlbnRyYXRpb25zIGRhdGFzZXQgKFtjb25jZW50cmF0aW9ucyBkYXRhc2V0IHVzZWRdKCNyZWFkX2NvbmNlbnRyYXRpb25zX2V4KSkuCgpfXzE6X18gU3RhdGlzdGljcyBvbiBhbGwgdmFyaWFibGVzOgoKYGBge3J9CnZhcnMuc3RhdHM9c3RhdHNfYnlfdmFyaWFibGUoY29uY2VudHJhdGlvbnNfZGF0YXNldCkKRFQ6OmRhdGF0YWJsZSh2YXJzLnN0YXRzLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWD1UUlVFKSkKYGBgCgoKCiMgUHJlLVByb2Nlc3NpbmcKCiMjIE1pc3NpbmcgVmFsdWVzIDxhIG5hbWU9Im1pc3NpbmdfdmFsdWVzIj48L2E+CgpUaGUgdHJlYXRtZW50IG9mIG1pc3NpbmcgdmFsdWVzIGNhbiBiZSBkb25lIGJ5IHJlcGxhY2luZyB0aGVtIHdpdGggYW5vdGhlciB2YWx1ZS4KCj4gbWlzc2luZ3ZhbHVlc19pbXB1dGF0aW9uKGRhdGFzZXQsIG1ldGhvZCA9ICJ2YWx1ZSIsIHZhbHVlID0gNWUtMDQsIGsgPSA1KQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGhvZF86IG1ldGhvZCBieSB3aGljaCB0byB0cmVhdCB0aGUgbWlzc2luZyB2YWx1ZXMuIEl0IGNhbiBlaXRoZXIgYmUKICAKICAgICAgKyAidmFsdWUiOiByZXBsYWNlcyB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCBhIHNwZWNpZmljIHZhbHVlIChnaXZlbiBpbiBfdmFsdWVfIGFyZ3VtZW50KTsKICAgICAgCiAgICAgICsgIm1lYW4iOiByZXBsYWNlcyB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgbWVhbiBvZiB0aGUgdmFyaWFibGVzJyB2YWx1ZXM7CiAgICAgIAogICAgICArICJtZWRpYW4iOiByZXBsYWNlcyB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgbWVkaWFuIG9mIHRoZSB2YXJpYWJsZXMnIHZhbHVlcwogICAgICAKICAgICAgKyAia25uIjogcmVwbGFjZXMgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggayBuZWFyZXN0IG5laWdoYm9yIChnaXZlbiBpbiBfa18gYXJndW1lbnQpIGF2ZXJhZ2luZzsKICAgICAgCiAgICAgICsgImxpbmFwcHJveCI6IHJlcGxhY2VzIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIGxpbmVhciBhcHByb3hpbWF0aW9uLgoKICAqIF92YWx1ZV86IHRoZSB2YWx1ZSB0byByZXBsYWNlIHRoZSBtaXNzaW5nIHZhbHVlcyBpZiB0aGUgbWV0aG9kICJ2YWx1ZSIgaXMgY2hvc2VuOwogIAogICogX2tfOiB0aGUgbnVtYmVyIG9mIG5laWdoYm9ycyBpZiB0aGUgbWV0aG9kICJrbm4iIGlzIGNob3Nlbi4KICAKICAKICAKIyMgUmVtb3ZlIERhdGEKCiMjIyBSZW1vdmUgZGF0YSB2YXJpYWJsZXMKCiMjIyMgRnVuY3Rpb24gdG8gdXNlCgoKX19Zb3UgY2FuIHJlbW92ZSBzcGVjaWZpYyBkYXRhIHZhcmlhYmxlcyBmcm9tIHRoZSBkYXRhc2V0X18KCj4gcmVtb3ZlX2RhdGFfdmFyaWFibGVzKGRhdGFzZXQsIHZhcmlhYmxlcy50by5yZW1vdmUsIGJ5LmluZGV4ID0gRkFMU0UpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfdmFyaWFibGVzLnRvLnJlbW92ZV86IHZlY3RvciB3aXRoIHRoZSBpbmRleGVzIG9yIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgdG8gcmVtb3ZlOwogIAogICogX2J5LmluZGV4XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgdmFsdWVzIGluIF92YXJpYWJsZXMudG8ucmVtb3ZlXyBhcmd1bWVudCBhcmUgaW5kZXhlcyBvciBub3QuIERlZmF1bHRzIHRvIEZBTFNFOwoKX19Zb3UgY2FuIHJlbW92ZSBhbiBpbnRlcnZhbCBvZiBkYXRhIHZhcmlhYmxlcy4gU2hvdWxkIG5vdCBiZSB1c2VkIGZvciBjb25jZW50cmF0aW9ucyBkYXRhX18KCj4gcmVtb3ZlX3hfdmFsdWVzX2J5X2ludGVydmFsKGRhdGFzZXQsIG1pbi52YWx1ZSwgbWF4LnZhbHVlKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21pbi52YWx1ZV86IHRoZSBtaW5pbXVtIHZhbHVlIG9mIHRoZSBpbnRlcnZhbDsKCiAgKiBfbWF4LnZhbHVlXzogdGhlIG1heGltdW0gdmFsdWUgb2YgdGhlIGludGVydmFsLgoKIyMjIyBFeGFtcGxlCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhIGNvbmNlbnRyYXRpb25zIGRhdGFzZXQgKFtjb25jZW50cmF0aW9ucyBkYXRhc2V0IHVzZWRdKCNyZWFkX2NvbmNlbnRyYXRpb25zX2V4KSkuCgpfXzEuX18gUmVtb3ZlIHRoZSB2YXJpYWJsZXMgX0NyZWF0aW5lXyBhbmQgX1NlcmluZV8gZnJvbSB0aGUgZGF0YXNldDoKCmBgYHtyfQpyZW1vdmVfdmFyc19jb25jZW50cmF0aW9uc19kYXRhc2V0ID0gcmVtb3ZlX2RhdGFfdmFyaWFibGVzKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIGMoIkNyZWF0aW5lIiwiU2VyaW5lIikpCmBgYAoKX18yLl9fIERhdGEgdGFibGUgYWZ0ZXIgcmVtb3ZpbmcgdGhlIHZhcmlhYmxlcyBmcm9tIHRoZSBkYXRhc2V0OgoKYGBge3J9CkRUOjpkYXRhdGFibGUocmVtb3ZlX3ZhcnNfY29uY2VudHJhdGlvbnNfZGF0YXNldCRkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCgojIyMgUmVtb3ZlIG1ldGFkYXRhIHZhcmlhYmxlcwoKIyMjIyBGdW5jdGlvbiB0byB1c2UKCj4gcmVtb3ZlX21ldGFkYXRhX3ZhcmlhYmxlcyhkYXRhc2V0LCB2YXJpYWJsZXMudG8ucmVtb3ZlKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3ZhcmlhYmxlcy50by5yZW1vdmVfOiB2ZWN0b3Igd2l0aCB0aGUgbmFtZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcyB0byByZW1vdmUuCgojIyMjIEV4YW1wbGUKCkV4YW1wbGUgbWFrZXMgdXNlIG9mIGFuIElSIGRhdGFzZXQgKFtJUiBkYXRhc2V0IHVzZWRdKCNyZWFkX2lyX2V4KSkuCgpfXzEuX18gUmVtb3ZlIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBfdmFyaWV0aWVzXyBmcm9tIHRoZSBkYXRhc2V0OgoKYGBge3J9CnJlbW92ZV9tZXRhX2lyX3NwZWN0cmFfZGF0YXNldD1yZW1vdmVfbWV0YWRhdGFfdmFyaWFibGVzKGlyX3NwZWN0cmFfZGF0YXNldCwgInZhcmlldGllcyIpCmBgYAoKX18yLl9fIE1ldGFkYXRhIHRhYmxlIGFmdGVyIHJlbW92aW5nIHRoZSBtZXRhZGF0YSB2YXJpYWJsZXMgZnJvbSB0aGUgZGF0YXNldDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlbW92ZV9tZXRhX2lyX3NwZWN0cmFfZGF0YXNldCRtZXRhZGF0YSwgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFKSkKYGBgCgoKIyMjIFJlbW92ZSBzYW1wbGVzCgojIyMjIEZ1bmN0aW9uIHRvIHVzZQoKPiByZW1vdmVfc2FtcGxlcyhkYXRhc2V0LCBzYW1wbGVzLnRvLnJlbW92ZSwgcmVidWlsZC5mYWN0b3JzID0gVCkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9zYW1wbGVzLnRvLnJlbW92ZV86IHZlY3RvciB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgc2FtcGxlcyB0byByZW1vdmUgZnJvbSB0aGUgZGF0YXNldDsKICAKICAqIF9yZWJ1aWxkLmZhY3RvcnNfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBtZXRhZGF0YSBmYWN0b3JzIHNob3VsZCBiZSByZWJ1aWxkZWQgYWZ0ZXIgdGhlIHN1YnNldC4gRGVmYXVsdHMgdG8gVFJVRS4KCiMjIyMgRXhhbXBsZQoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYSBjb25jZW50cmF0aW9ucyBkYXRhc2V0IChbY29uY2VudHJhdGlvbnMgZGF0YXNldCB1c2VkXSgjcmVhZF9jb25jZW50cmF0aW9uc19leCkpLgoKX18xLl9fIFJlbW92ZSB0aGUgdmFyaWFibGVzIF9QSUZcXzE3OF8gYW5kIF9QSUZcXzA5MF8gZnJvbSB0aGUgZGF0YXNldDoKCmBgYHtyfQpyZW1vdmVfc2FtcHNfY29uY2VudHJhdGlvbnNfZGF0YXNldCA9IHJlbW92ZV9zYW1wbGVzKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIGMoIlBJRl8xNzgiLCJQSUZfMDkwIikpCmBgYAoKX18yLl9fIERhdGEgdGFibGUgYWZ0ZXIgcmVtb3ZpbmcgdGhlIHNhbXBsZXMgZnJvbSB0aGUgZGF0YXNldDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlbW92ZV9zYW1wc19jb25jZW50cmF0aW9uc19kYXRhc2V0JGRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCkRUOjpkYXRhdGFibGUocmVtb3ZlX3NhbXBzX2NvbmNlbnRyYXRpb25zX2RhdGFzZXQkbWV0YWRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCmBgYAoKCgojIyBSZW1vdmUgRGF0YSBhY2NvcmRpbmcgdG8gcHJlc2VuY2Ugb2YgbWlzc2luZyB2YWx1ZXMKCiMjIyBSZW1vdmUgc2FtcGxlcyAKCiMjIyMgRnVuY3Rpb25zIHRvIHVzZQoKX19Zb3UgY2FuIHJlbW92ZSBzYW1wbGVzIGFjY29yZGluZyB0byB0aGUgYW1vdW50IG9mIG1pc3NpbmcgdmFsdWVzIHByZXNlbnQgaW4gZWFjaCBzYW1wbGVfXwoKPiByZW1vdmVfc2FtcGxlc19ieV9uYXMoZGF0YXNldCwgbWF4Lm5hcyA9IDAsIGJ5LnBlcmNlbnQgPSBGKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21heC5uYXNfOiBtYXhpbXVtIG51bWJlciBvciBwZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzIHRoYXQgYSBzYW1wbGUgY2FuIGhhdmUuIFNhbXBsZXMgd2l0aCBtb3JlIG1pc3NpbmcgdmFsdWVzIHRoYW4gdGhlIG9uZXMgYWxsb3dlZCBhcmUgcmVtb3ZlZC4gRGVmYXVsdHMgdG8gMDsKICAKICAqIF9ieS5wZXJjZW50XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgdmFsdWUgb2YgdGhlIF9tYXgubmFzXyBhcmd1bWVudCBpcyBhIHBlcmNlbnRhZ2Ugb3Igbm90LiBEZWZhdWx0cyB0byBGQUxTRS4KCl9fWW91IGNhbiByZW1vdmUgc2FtcGxlcyBpZiB0aGUgY29ycmVzcG9uZGluZyB2YWx1ZSBvZiBhIG1ldGFkYXRhIHZhcmlhYmxlIGlzIG1pc3NpbmdfXwoKPiByZW1vdmVfc2FtcGxlc19ieV9uYV9tZXRhZGF0YShkYXRhc2V0LCBtZXRhZGF0YS52YXIpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0YWRhdGEudmFyXzogbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgYnkgd2hpY2ggdGhlIHNhbXBsZXMgY291bGQgYmUgcmVtb3ZlZCBpZiB0aGUgdmFsdWUgaXMgbWlzc2luZy4KCgojIyMgUmVtb3ZlIHZhcmlhYmxlcwoKIyMjIyBGdW5jdGlvbiB0byB1c2UKCl9fWW91IGNhbiByZW1vdmUgdmFyaWFibGVzIGFjY29yZGluZyB0byB0aGUgYW1vdW50IG9mIG1pc3NpbmcgdmFsdWVzIHByZXNlbnQgaW4gZWFjaCB2YXJpYWJsZV9fCgo+IHJlbW92ZV92YXJpYWJsZXNfYnlfbmFzKGRhdGFzZXQsIG1heC5uYXMgPSAwLCBieS5wZXJjZW50ID0gRikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9tYXgubmFzXzogbWF4aW11bSBudW1iZXIgb3IgcGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyB0aGF0IGEgdmFyaWFibGUgY2FuIGhhdmUuIFZhcmlhYmxlcyB3aXRoIG1vcmUgbWlzc2luZyB2YWx1ZXMgdGhhbiB0aGUgb25lcyBhbGxvd2VkIGFyZSByZW1vdmVkLiBEZWZhdWx0cyB0byAwOwogIAogICogX2J5LnBlcmNlbnRfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSB2YWx1ZSBvZiB0aGUgX21heC5uYXNfIGFyZ3VtZW50IGlzIGEgcGVyY2VudGFnZSBvciBub3QuIERlZmF1bHRzIHRvIEZBTFNFLgoKCgoKIyMgRGF0YSBUcmFuc2Zvcm1hdGlvbgoKVGhlIGRhdGEgY2FuIGJlIHRyYW5zZm9ybWVkIGJ5IG9uZSBvZiB0d28gbWV0aG9kczogbG9nYXJpdGhtaWMgdHJhbnNmb3JtYXRpb24gb3IgY3ViaWMgcm9vdCB0cmFuc2Zvcm1hdGlvbi4KCj4gdHJhbnNmb3JtX2RhdGEoZGF0YXNldCwgbWV0aG9kID0gImxvZyIpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0aG9kXzogc3RyaW5nIHNwZWNpZnlpbmcgdGhlIHRyYW5zZm9ybWF0aW9uIG1ldGhvZC4gSXQgY2FuIGVpdGhlciBiZQogIAogICAgICArICJsb2ciOiBsb2dhcml0aG1pYyB0cmFuc2Zvcm1hdGlvbjsKICAgICAgCiAgICAgICsgImN1Ymljcm9vdCI6IGN1YmljIHJvb3QgdHJhbnNmb3JtYXRpb24uCgojIyBTY2FsaW5nCgpUaGUgZGF0YSBjYW4gYmUgc2NhbGVkIGFjY29yZGluZyB0byBvbmUgb2YgdGhyZWUgbWV0aG9kczogYXV0bywgcmFuZ2Ugb3IgcGFydG8gc2NhbGluZy4KCj4gc2NhbGluZyhkYXRhc2V0LCBtZXRob2QgPSAiYXV0byIpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0aG9kXzogc3RyaW5nIHNwZWNpZnlpbmcgdGhlIHNjYWxpbmcgbWV0aG9kLiBJdCBjYW4gZWl0aGVyIGJlICJhdXRvIiwgInJhbmdlIiBvciAicGFyZXRvIi4KCiMjIENvcnJlY3Rpb24KClRocmVlIG1ldGhvZHMgYXJlIGF2YWlsYWJsZSB0byBwZXJmb3JtIGRhdGEgY29ycmVjdGlvbjogYmFja2dyb3VuZCwgb2Zmc2V0IGFuZCBiYXNlbGluZSBjb3JyZWN0aW9ucy4gVGhpcyBzaG91bGQgb25seSBiZSBhcHBsaWVkIHRvIHNwZWN0cmFsIGRhdGEuCgo+IGRhdGFfY29ycmVjdGlvbihkYXRhc2V0LCB0eXBlID0gImJhY2tncm91bmQiLCBtZXRob2QgPSAibW9kcG9seWZpdCIsIC4uLikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF90eXBlXzogc3RyaW5nIHNwZWNpZnlpbmcgdGhlIHR5cGUgb2YgY29ycmVjdGlvbiB0aGF0IHdpbGwgYmUgYXBwbGllZC4gSXQgY2FuIGVpdGhlciBiZSAiYmFja2dyb3VuZCIsICJvZmZzZXQiLCAiYmFzZWxpbmUiOwogIAogICogX21ldGhvZF86IHdoZW4gImJhc2VsaW5lIiBjb3JyZWN0aW9uIGlzIGNob3NlbiwgeW91IGNhbiBjaG9vc2UgdGhlIGJhc2VsaW5lIG1ldGhvZCB0byB1c2UuIEl0IGNhbiBlaXRoZXIgYmUgWyJhbHMiXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYmFzZWxpbmUvdmVyc2lvbnMvMS4yLTEvdG9waWNzL2Jhc2VsaW5lLmFscyksIFsiZmlsbFBlYWtzIl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2VsaW5lL3ZlcnNpb25zLzEuMi0xL3RvcGljcy9iYXNlbGluZS5maWxsUGVha3MpLCBbImlybHMiXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYmFzZWxpbmUvdmVyc2lvbnMvMS4yLTEvdG9waWNzL2Jhc2VsaW5lLmlybHMpLCBbImxvd3Bhc3MiXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYmFzZWxpbmUvdmVyc2lvbnMvMS4yLTEvdG9waWNzL2Jhc2VsaW5lLmxvd3Bhc3MpLCBbIm1lZGlhbldpbmRvdyJdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlbGluZS92ZXJzaW9ucy8xLjItMS90b3BpY3MvYmFzZWxpbmUubWVkaWFuV2luZG93KSwgWyJtb2Rwb2x5Zml0Il0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2VsaW5lL3ZlcnNpb25zLzEuMi0xL3RvcGljcy9iYXNlbGluZS5tb2Rwb2x5Zml0KSwgWyJwZWFrRGV0ZWN0aW9uIl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2VsaW5lL3ZlcnNpb25zLzEuMi0xL3RvcGljcy9iYXNlbGluZS5wZWFrRGV0ZWN0aW9uKSwgWyJyZmJhc2VsaW5lIl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2VsaW5lL3ZlcnNpb25zLzEuMi0xL3RvcGljcy9iYXNlbGluZS5yZmJhc2VsaW5lKSwgWyJyb2xsaW5nQmFsIl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2VsaW5lL3ZlcnNpb25zLzEuMi0xL3RvcGljcy9iYXNlbGluZS5yb2xsaW5nQmFsbCksIFsic2hpcmxleSJdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlbGluZS92ZXJzaW9ucy8xLjItMS90b3BpY3MvYmFzZWxpbmUuc2hpcmxleSkuIERlZmF1bHRzIHRvICJtb2Rwb2x5Zml0IjsKICAKICAqIF8uLi5fOiBBZGRpdGlvbmFsIHBhcmFtZXRlcnMgdGhhdCBtYXkgYmUgc2V0LCBhY2NvcmRpbmcgdG8gdGhlIG1ldGhvZCBjaG9zZW4uIFNlZSB0aGUgbGlua3MgcHJvdmlkZWQgaW4gZWFjaCBtZXRob2QgYWJvdmUgZm9yIGZ1cnRoZXIgaW5mb3JtYXRpb24sIGFzIHRoZXNlIGFyZSBtZXRob2RzIGRldmVsb3BlZCBieSB0aGUgW2Jhc2VsaW5lXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYmFzZWxpbmUvdmVyc2lvbnMvMS4yLTEpIHBhY2thZ2UuCiAgCiAgCgojIyBTbW9vdGhpbmcgSW50ZXJwb2xhdGlvbgoKU21vb3RoaW5nIGludGVycG9sYXRpb24gd2l0aCBtZXRob2RzIEJpbiwgTG9lc3MgYW5kIFNhdml0emt5LUdvbGF5LgoKPiBzbW9vdGhpbmdfaW50ZXJwb2xhdGlvbihkYXRhc2V0LCBtZXRob2QgPSAiYmluIiwgcmVkdWNpbmcuZmFjdG9yID0gMiwgeC5heGlzID0gTlVMTCwgcC5vcmRlciA9IDMsIHdpbmRvdyA9IDExLCBkZXJpdiA9IDApCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0aG9kXzogc3RyaW5nIHNwZWNpZnlpbmcgdGhlIHNtb290aGluZyBtZXRob2QuIEl0IGNhbiBlaXRoZXIgYmUgImJpbiIsICJsb2VzcyIsICJzYXZpdHpreS5nb2xheSIuIEl0IGRlZmF1bHRzIHRvICJiaW4iOwogIAogICogX3JlZHVjaW5nLmZhY3Rvcl86IGlmIG1ldGhvZCAiYmluIiBpcyBjaG9zZW4sIHRoZSBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgdGhlIHJlZHVjaW5nIGZhY3RvciBzaG91bGQgYmUgZ2l2ZW4uIEl0IGRlZmF1bHRzIHRvIDI7CiAgCiAgKiBfeC5heGlzXzogaWYgbWV0aG9kICJsb2VzcyIgaXMgY2hvc2VuLCBhIG51bWVyaWMgdmVjdG9yIHJlcHJlc2VudGluZyB0aGUgbmV3IHgtYXhpcyBmb3IgbG9lc3MgbWV0aG9kIGNhbiBiZSBnaXZlbi4gRGVmYXVsdHMgdG8gTlVMTDsKICAKICAqIF9wLm9yZGVyXzogaWYgbWV0aG9kICJzYXZpdHpreS5nb2xheSIgaXMgY2hvc2VuLCBhIG51bWVyaWMgdmFsdWUgcmVwcmVzZW50aW5nIHRoZSBwb2x5bm9taWFsIG9yZGVyIHNob3VsZCBiZSBnaXZlbi4gSXQgZGVmYXVsdHMgdG8gMzsKICAKICAqIF93aW5kb3dfOiBpZiBtZXRob2QgInNhdml0emt5LmdvbGF5IiBpcyBjaG9zZW4sIGEgb2RkIG51bWVyaWMgdmFsdWUgaW5kaWNhdGluZyB0aGUgc2l6ZSBvZiB0aGUgd2luZG93LiBJdCBkZWZhdWx0cyB0byAxMTsKICAKICAqIF9kZXJpdl86IGlmIG1ldGhvZCAic2F2aXR6a3kuZ29sYXkiIGlzIGNob3NlbiwgYSBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgdGhlIGRpZmZlcmVudGlhdGlvbiBvcmRlci4gSXQgZGVmYXVsdHMgdG8gMC4KCiMjIENvbnZlcnQgdG8gRmFjdG9yIDxhIG5hbWU9ImNvbnZlcnRfdG9fZmFjdG9yIj48L2E+CgpNZXRhZGF0YSB2YXJpYWJsZXMgY2FuIGJlIGNvbnZlcnRlZCB0byBmYWN0b3JzICh1c2VmdWwgaW4gc29tZSBhbmFseXNpcywgZm9yIGV4YW1wbGUsIHRyYWluIGNsYXNzaWZjYXRpb24gbW9kZWxzIGFuZCBjb2xvciBwbG90cyBiYXNlZCBvbiBhIG1ldGFkYXRhIHZhcmlhYmxlKToKCj4gY29udmVydF90b19mYWN0b3IoZGF0YXNldCwgbWV0YWRhdGEudmFyKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGFkYXRhLnZhcl86IG5hbWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIHRvIGNvbnZlcnQuCgoKCiMjIE1lYW4gQ2VudGVyaW5nCgo+IG1lYW5fY2VudGVyaW5nKGRhdGFzZXQpCgogICogX2RhdGFzZXRfOiBhIHNlcGNtaW5lIGRhdGFzZXQuCgojIyBGaXJzdCBEZXJpdmF0aXZlCgo+IGZpcnN0X2Rlcml2YXRpdmUoZGF0YXNldCkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldC4KCiMjIE11bHRpcGxpY2F0aXZlIFNjYXR0ZXIgQ29ycmVjdGlvbgoKPiBtc2NfY29ycmVjdGlvbihkYXRhc2V0KQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0LgoKIyMgRGF0YSBOb3JtYWxpemF0aW9uCgpEYXRhIG5vcm1hbGl6YXRpb24gY2FuIGJlIHBlcmZvcm1lZCB0aHJvdWdoIHRoZSBzdW0sIG1lZGlhbiwgYSByZWZlcmVuY2Ugc2FtcGxlIG9yIGEgcmVmZXJlbmNlIHZhcmlhYmxlCgo+IG5vcm1hbGl6ZShkYXRhc2V0LCBtZXRob2QsIHJlZiA9IE5VTEwsIGNvbnN0YW50ID0gMTAwMCkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF8gbWV0aG9kXzogc3RyaW5nIHNwZWNpZnlpbmcgdGhlIG5vcm1hbGl6YXRpb24gbWV0aG9kLiBJdCBjYW4gZWl0aGVyIGJlIAogICAgCiAgICAgICsgInN1bSI6IG5vcm1hbGl6YXRpb24gYnkgdGhlIHN1bSBvZiBhIGNvbnN0YW50LCBnaXZlbiBpbiB0aGUgX2NvbnN0YW50XyBhcmd1bWVudDsKICAgICAgCiAgICAgICsgIm1lZGlhbiI6IG5vcm1hbGl6YXRpb24gYnkgdGhlIG1lZGlhbjsKICAgICAgCiAgICAgICsgInJlZi5zYW1wbGUiOiBub3JtYWxpemF0aW9uIGJ5IGEgcmVmZXJlbmNlIHNhbXBsZSwgZ2l2ZW4gaW4gdGhlIF9yZWZfIGFyZ3VtZW50OwogICAgICAKICAgICAgKyAicmVmLmZlYXR1cmUiOiBub3JtYWxpemF0aW9uIGJ5IGEgcmVmZXJlbmNlIHZhcmlhYmxlLCBnaXZlbiBpbiB0aGUgX3JlZl8gYXJndW1lbnQuCiAgICAgIAogICogX3JlZl86IGlmIG1ldGhvZCAicmVmLnNhbXBsZSIgb3IgcmVmLmZlYXR1cmUiIiBpcyBjaG9zZW4sIGEgc3RyaW5nIGluZGljYXRpbmcgdGhlIHNhbXBsZSBvciB2YXJpYWJsZSBvZiByZWZlcmVuY2UgbXVzdCBiZSBnaXZlbjsKICAKICAqIF9jb25zdGFudF86IGlmIG1ldGhvZCAic3VtIiBpcyBjaG9zZW4sIHRoZSBjb25zdGFudCBieSB3aGljaCB0byBkbyB0aGUgc3VtIG5vcm1hbGl6YXRpb24gbXVzdCBiZSBnaXZlbi4KCiMjIFN1YnNldCBkYXRhc2V0IGJ5IHZhcmlhYmxlcwoKIyMjIEZ1bmN0aW9ucyB0byB1c2UKCl9fWW91IGNhbiBzdWJzZXQgdGhlIGRhdGFzZXQgYnkgb25seSBrZWVwaW5nIGRhdGEgZnJvbSBzcGVjaWZpYyB2YXJpYWJsZXNfXwoKPiBzdWJzZXRfeF92YWx1ZXMoZGF0YXNldCwgdmFyaWFibGVzLCBieS5pbmRleCA9IEZBTFNFKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3ZhcmlhYmxlc186IHZlY3RvciB3aXRoIHRoZSBpbmRleGVzIG9yIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgdG8ga2VlcDsKICAKICAqIF9ieS5pbmRleF86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIHZhbHVlcyBpbiBfdmFyaWFibGVzXyBhcmd1bWVudCBhcmUgaW5kZXhlcyBvciBub3QuIERlZmF1bHRzIHRvIEZBTFNFOwogIApfX1lvdSBjYW4gc3Vic2V0IHRoZSBkYXRhc2V0IGJ5IGtlZXBpbmcgZGF0YSBmcm9tIGFuIGludGVydmFsIG9mIHZhcmlhYmxlcy4gT25seSBmb3IgZGF0YSB3aG9zZSB2YXJpYWJsZXMgYXJlIG51bWVyaWMsIGkuZS4sIHNob3VsZCBub3QgYmUgdXNlZCBmb3IgY29uY2VudHJhdGlvbnMgZGF0YV9fCgo+IHN1YnNldF94X3ZhbHVlc19ieV9pbnRlcnZhbChkYXRhc2V0LCBtaW4udmFsdWUsIG1heC52YWx1ZSkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9taW4udmFsdWVfOiB0aGUgbWluaW11bSB2YWx1ZSBvZiB0aGUgaW50ZXJ2YWw7CiAgCiAgKiBfbWF4LnZhbHVlXzogdGhlIG1heGltdW0gdmFsdWUgb2YgdGhlIGludGVydmFsLgoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBVVi1WaXMgZGF0YXNldCAoW1VWLVZpcyBkYXRhc2V0IHVzZWRdKCNyZWFkX3V2dl9leCkpLgoKX18xLl9fIFN1YnNldCBkYXRhc2V0IHRvIG9ubHkga2VlcCB0aGUgd2F2ZWxlbmd0aCB2YWx1ZXMgYmV0d2VlbiA0MDAgYW5kIDYwMDoKCmBgYHtyfQpzdWJzZXRfdmFsc19pbnRfdXZ2aXNfc3BlY3RyYV9kYXRhc2V0PXN1YnNldF94X3ZhbHVlc19ieV9pbnRlcnZhbCh1dnZpc19zcGVjdHJhX2RhdGFzZXQsIDQwMCwgNjAwKQpgYGAKCl9fMi5fXyBEYXRhIGFuZCBtZXRhZGF0YSB0YWJsZXMgYWZ0ZXIgc3Vic2V0dGluZyB0aGUgZGF0YXNldDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHN1YnNldF92YWxzX2ludF91dnZpc19zcGVjdHJhX2RhdGFzZXQkZGF0YSwgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFKSkKRFQ6OmRhdGF0YWJsZShzdWJzZXRfdmFsc19pbnRfdXZ2aXNfc3BlY3RyYV9kYXRhc2V0JG1ldGFkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCgoKCiMjIFN1YnNldCBkYXRhc2V0IGJ5IHNhbXBsZXMKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgpfX1lvdSBjYW4gc3Vic2V0IHRoZSBkYXRhc2V0IGJ5IG9ubHkga2VlcGluZyBkYXRhIGZyb20gc3BlY2lmaWMgc2FtcGxlc19fCgo+IHN1YnNldF9zYW1wbGVzKGRhdGFzZXQsIHNhbXBsZXMsIHJlYnVpbGQuZmFjdG9ycyA9IFQpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGF0c2V0OwogIAogICogX3NhbXBsZXNfOiB2ZWN0b3Igd2l0aCB0aGUgaW5kZXhlcyBvciBuYW1lcyBvZiB0aGUgc2FtcGxlcyB0byBrZWVwOwogIAogICogX3JlYnVpbGQuZmFjdG9yc186IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgdGhlIG1ldGFkYXRhIGZhY3RvcnMgc2hvdWxkIGJlIHJlYnVpbGRlZCBhZnRlciB0aGUgc3Vic2V0LiBEZWZhdWx0cyB0byBUUlVFLgogIApfX1lvdSBjYW4gc3Vic2V0IHRoZSBkYXRhc2V0IGJ5IGtlZXBpbmcgc2FtcGxlcyB0aGF0IGhhdmUgY2VydGFpbiBtZXRhZGF0YSBjbGFzc2UocylfXwoKPiBzdWJzZXRfc2FtcGxlc19ieV9tZXRhZGF0YV92YWx1ZXMoZGF0YXNldCwgbWV0YWRhdGEudmFybmFtZSwgdmFsdWVzKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGFkYXRhLnZhcm5hbWVfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBieSB3aG9zZSB2YWx1ZXMgeW91IHdpbGwgd2FudCB0byBrZWVwIHRoZSBzYW1wbGVzOwogIAogICogX3ZhbHVlc186IHRoZSBzYW1wbGVzIHRoYXQgYXJlIGtlcHQgbXVzdCBoYXZlIHRoZSBtZXRhZGF0YSBjbGFzc2Uocykgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIGNob3NlbiAobWV0YWRhdGEudmFybmFtZSkgc3BlY2lmaWVkIGluIHRoaXMgYXJndW1lbnQsIHVzaW5nIGEgY2hhcmFjdGVyIHZlY3RvciB3aXRoIHRoZSBjbGFzc2UocykuCgojIyMgRXhhbXBsZXMKCkV4YW1wbGUgbWFrZXMgdXNlIG9mIGFuIG5tci1wZWFrcyBkYXRhc2V0IChbTk1SIHBlYWtzIGRhdGFzZXQgdXNlZF0oI3JlYWRfbm1yX3BlYWtzX2V4KSkuCgpfXzEuX18gU3Vic2V0IGRhdGFzZXQgdG8gb25seSBrZWVwIHRoZSBzYW1wbGVzIF9BQ1xfYXVfLCBfQUNcX3NtXywgX0FDXF9zcF8sIF9BQ1xfd2lfOiAKCmBgYHtyfQpzdWJzZXRfc2FtcF9ubXJfcGVha3NfZGF0YXNldD1zdWJzZXRfc2FtcGxlcyhubXJfcGVha3NfZGF0YXNldCwgYygiQUNfYXUiLCAiQUNfc20iLCAiQUNfc3AiLCAiQUNfd2kiKSkKYGBgCgpfXzIuX18gRGF0YSBhbmQgbWV0YWRhdGEgdGFibGVzIGFmdGVyIHN1YnNldHRpbmcgdGhlIGRhdGFzZXQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShzdWJzZXRfc2FtcF9ubXJfcGVha3NfZGF0YXNldCRkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpEVDo6ZGF0YXRhYmxlKHN1YnNldF9zYW1wX25tcl9wZWFrc19kYXRhc2V0JG1ldGFkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCl9fMy5fXyBTdWJzZXQgZGF0YXNldCB0byBvbmx5IGtlZXAgc2FtcGxlcyBmcm9tIHdpbnRlciAoX3dpXykgYW5kIHN1bW1lciAoX3NtXykgc2Vhc29uczoKCmBgYHtyfQpzdWJzZXRfc2FtcF9tZXRfbm1yX3BlYWtzX2RhdGFzZXQ9c3Vic2V0X3NhbXBsZXNfYnlfbWV0YWRhdGFfdmFsdWVzKG5tcl9wZWFrc19kYXRhc2V0LCAic2Vhc29ucyIsIGMoIndpIiwgInNtIikpCmBgYAoKX180Ll9fIERhdGEgYW5kIG1ldGFkYXRhIHRhYmxlcyBhZnRlciBzdWJzZXR0aW5nIHRoZSBkYXRhc2V0OgoKYGBge3J9CkRUOjpkYXRhdGFibGUoc3Vic2V0X3NhbXBfbWV0X25tcl9wZWFrc19kYXRhc2V0JGRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCkRUOjpkYXRhdGFibGUoc3Vic2V0X3NhbXBfbWV0X25tcl9wZWFrc19kYXRhc2V0JG1ldGFkYXRhLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWCA9IFRSVUUpKQpgYGAKCgoKCgojIyBTdWJzZXQgZGF0YXNldCBieSBzYW1wbGVzIGFuZCB2YXJpYWJsZXMKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgpfX1lvdSBjYW4gc3Vic2V0IHRoZSBkYXRhc3QgYnkga2VlcGluZyBzcGVjaWZpYyBzYW1wbGVzIGFuZCB2YXJpYWJsZXNfXwoKPiBzdWJzZXRfYnlfc2FtcGxlc19hbmRfeHZhbHVlcyhkYXRhc2V0LCBzYW1wbGVzLCB2YXJpYWJsZXMgPSBOVUxMLCBieS5pbmRleCA9IEYsIHZhcmlhYmxlLmJvdW5kcyA9IE5VTEwsIHJlYnVpbGQuZmFjdG9ycyA9IFQpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfc2FtcGxlc186IHZlY3RvciB3aXRoIHRoZSBpbmRleGVzIG9yIG5hbWVzIG9mIHRoZSBzYW1wbGVzIHRvIGtlZXA7CiAgCiAgKiBfdmFyaWFibGVzXzogdmVjdG9yIHdpdGggdGhlIGluZGV4ZXMgb3IgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyB0byBrZWVwLiBEZWZhdWx0cyB0byBOVUxMOwogIAogICogX2J5LmluZGV4XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgdmFsdWVzIGluIF92YXJpYWJsZXNfIGFuZCBfc2FtcGxlc18gYXJndW1lbnRzIGFyZSBpbmRleGVzIG9yIG5vdC4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfdmFyaWFibGUuYm91bmRzXzogbnVtZXJpYyB2ZWN0b3Igb2YgdHdvIGVsZW1lbnRzIHdpdGggdGhlIG1pbmltdW0gYW5kIG1heGltdW0gdmFsdWVzIG9mIHRoZSBpbnRlcnZhbCBvZiB2YXJpYWJsZXMgdG8ga2VlcC4gQXJndW1lbnQgc2hvdWxkIG5vdCBiZSB1c2VkIGZvciBjb25jZW50cmF0aW9ucyBkYXRhc2V0LiBXaGVuIHVzZWQsIGFyZ3VtZW50cyBfYnkuaW5kZXhfIHNob3VsZCBiZSBGQUxTRSBhbmQgX3ZhcmlhYmxlc18gTlVMTC4gRGVmYXVsdHMgdG8gTlVMTDsKICAKICAqIF9yZWJ1aWxkLmZhY3RvcnNfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIG1ldGFkYXRhIGZhY3RvcnMgc2hvdWxkIGJlIHJlYnVpbGRlZC4gRGVmYXVsdHMgdG8gVFJVRS4KCgoKCiMjIyBFeGFtcGxlcwoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMS5fXyBTdWJzZXQgdGhlIGRhdGFzZXQgdG8gb25seSBrZWVwIHRoZSBkYXRhIGZyb20gc2FtcGxlcyBfUElGXF8xNzhfIGFuZCBfTkVUTFxfMDIyXF9WMV8sICBhbmQgZnJvbSB2YXJpYWJsZXMgX0NyZWF0aW5pbmVfIGFuZCBfU2VyaW5lXy4KCmBgYHtyfQpzdWJzZXRfdmFyc19zYW1wX2NvbmNlbnRyYXRpb25zX2RhdGFzZXQgPSBzdWJzZXRfYnlfc2FtcGxlc19hbmRfeHZhbHVlcyhjb25jZW50cmF0aW9uc19kYXRhc2V0LCBjKCJQSUZfMTc4IiwiTkVUTF8wMjJfVjEiKSwgdmFyaWFibGVzID0gYygiQ3JlYXRpbmUiLCJTZXJpbmUiKSkKYGBgCgpfXzIuX18gRGF0YSBhbmQgbWV0YWRhdGEgdGFibGVzIGFmdGVyIHN1YnNldHRpbmcgdGhlIGRhdGFzZXQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShzdWJzZXRfdmFyc19zYW1wX2NvbmNlbnRyYXRpb25zX2RhdGFzZXQkZGF0YSwgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFKSkKRFQ6OmRhdGF0YWJsZShzdWJzZXRfdmFyc19zYW1wX2NvbmNlbnRyYXRpb25zX2RhdGFzZXQkbWV0YWRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYID0gVFJVRSkpCmBgYAoKCgojIyBMb3ctbGV2ZWwgRGF0YSBGdXNpb24KCiMjIyBGdW5jdGlvbiB0byB1c2UKCkRhdGFzZXRzIGZyb20gZGlmZmVyZW50IHR5cGVzIG9mIGRhdGEgY2FuIGJlIGpvaW5lZCB0b2dldGhlci4gT25seSB0aGUgc2FtcGxlcyB0aGF0IGFyZSB0aGUgc2FtZSBpbiBib3RoIGRhdGFzZXRzIHdpbGwgYXBwZWFyIGluIHRoZSBmaW5hbCBvbmUuCgo+IGxvd19sZXZlbF9mdXNpb24oZGF0YXNldHMpCgogICogX2RhdGFzZXRzXzogTGlzdCBvZiBzcGVjbWluZSBkYXRhc2V0cyB0byBqb2luIGluIG9uZS4KCgoKIyMgQWdncmVnYXRlIFNhbXBsZXMKCiMjIyBGdW5jdGlvbiB0byB1c2UKCj4gYWdncmVnYXRlX3NhbXBsZXMoZGF0YXNldCwgaW5kZXhlcywgYWdncmVnLmZuID0gIm1lYW4iLCBtZXRhLnRvLnJlbW92ZSA9IGMoKSkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9pbmRleGVzXzogbnVtZXJpYyB2ZWN0b3IgaW5kaWNhdGluZyBob3cgc2FtcGxlcyB3aWxsIGJlIGdyb3VwZWQuIEZvciBleGFtcGxlLCB0aGUgdmVjdG9yIGMoMSwyLDIsMywxLDMpIGluZGljYXRlcyB0aGF0IHRoZSBmaXJzdCBhbmQgZmlmdGggc2FtcGxlcyBhcmUgZ3JvdXBlZCBpbiBvbmUsIHRoZSBzZWNvbmQgYW5kIHRoaXJkIGluIGFub3RoZXIgb25lLCBhbmQgdGhlIGZvcnRoIGFuZCBsYXN0IG9uZSBhcmUgYWdncmVnYXRlZCBpbiBhbm90aGVyIG9uZTsKICAKICAqIF9hZ2dyZWcuZm5fOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbiBieSB3aGljaCB0aGUgc2FtcGxlcyB3aWxsIGJlIGFnZ3JlZ2F0ZWQuIEl0IGNhbiBlaXRoZXIgYmUgIm1lYW4iLCAibWVkaWFuIiwgInN1bSIsICJtYXgiIChtYXhpbXVtIHZhbHVlKSBvciAibWluIiAobWluaW11bSB2YWx1ZSkuIEl0IGRlZmF1bHRzIHRvICJtZWFuIjsKICAKICAqIF9tZXRhLnRvLnJlbW92ZV86IHZlY3RvciB3aXRoIHRoZSBuYW1lKHMpIG9mIHRoZSB2YXJpYWJsZShzKSB5b3Ugd2FudCB0byByZW1vdmUgYWZ0ZXIgdGhlIGFnZ3JnYXRpb24uIF9fb3B0aW9uYWxfXwoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBubXItcGVha3MgZGF0YXNldCAoW05NUiBwZWFrcyBkYXRhc2V0IHVzZWRdKCNyZWFkX25tcl9wZWFrc19leCkpLgoKX18xLl9fIEluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSwgdGhlIHNhbXBsZXMgYXJlIGFnZ3JlZ2F0ZWQgYWNjb3JkaW5nIHRvIHRoZSBjbGFzc2VzIGluIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSAic2Vhc29ucyIsIGkuZS4sIHNhbXBsZXMgd2l0aCB0aGUgc2FtZSBjbGFzcyBvbiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgd2lsbCBiZSBncm91cGVkIHRvZ2V0aGVyLgoKYGBge3J9CiNOdW1iZXIgb2YgZGlmZmVyZW50IG1ldGFkYXRhIHZhcmlhYmxlJ3MgdmFsdWVzIHdpbGwgYmUgdGhlIG51bWJlciBvZiBkaWZmZXJlbnQgZ3JvdXBzIGluIHdoaWNoIHRoZSBzYW1wbGVzIHdpbGwgYmUgZ3JvdXBlZDoKeD0xOmxlbmd0aChsZXZlbHMobm1yX3BlYWtzX2RhdGFzZXQkbWV0YWRhdGFbWyJzZWFzb25zIl1dKSkKbmFtZXMoeCk9bGV2ZWxzKG5tcl9wZWFrc19kYXRhc2V0JG1ldGFkYXRhW1sic2Vhc29ucyJdXSkKaW5kZXhlcz1jKCkKZm9yIChtZXRhIGluIG5tcl9wZWFrc19kYXRhc2V0JG1ldGFkYXRhW1sic2Vhc29ucyJdXSl7CiAgaW5kZXhlcz1jKGluZGV4ZXMsIHhbbWV0YV0pCn0KCiNUaGUgImFncm9yZWdpb25zIiBtZXRhZGF0YSB2YXJpYWJsZSBpcyByZW1vdmVkIGFmdGVyIHNhbXBsZXMgYXJlIGdyb3VwZWQsIGFzIGl0IHN0b3BzIG1ha2luZyBzZW5zZSBmb3IgaXQgdG8gYmUgdGhlcmUKYWdncmVnYXRlZF9ubXJfcGVha3NfZGF0YXNldCA9IGFnZ3JlZ2F0ZV9zYW1wbGVzKG5tcl9wZWFrc19kYXRhc2V0LCBpbmRleGVzLCBtZXRhLnRvLnJlbW92ZT0iYWdyb3JlZ2lvbnMiKQpgYGAKCl9fMi5fXyBEYXRhIGFuZCBNZXRhZGF0YSB0YWJsZXMgYWZ0ZXIgYWdncmVnYXRpb246CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShhZ2dyZWdhdGVkX25tcl9wZWFrc19kYXRhc2V0JGRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYPVRSVUUpKQpEVDo6ZGF0YXRhYmxlKGFnZ3JlZ2F0ZWRfbm1yX3BlYWtzX2RhdGFzZXQkbWV0YWRhdGEsIG9wdGlvbnM9bGlzdChzY3JvbGxYPVRSVUUpKQpgYGAKCgoKCiMjIEZsYXQgUGF0dGVybiBGaWx0ZXIKCiMjIyBGdW5jdGlvbiB0byB1c2UKCj4gZmxhdF9wYXR0ZXJuX2ZpbHRlcihkYXRhc2V0LCBmaWx0ZXIuZnVuY3Rpb24gPSAiaXFyIiwgYnkucGVyY2VudCA9IFQsIGJ5LnRocmVzaG9sZCA9IEYsIHJlZC52YWx1ZSA9IDApCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfZmlsdGVyLmZ1bmN0aW9uXzogZnVuY3Rpb24gYnkgd2hpY2ggdG8gZmlsdGVyIHRoZSBkYXRhIHZhcmlhYmxlcy4gSXQgY2FuIGVpdGhlciBiZQogIAogICAgICArICJpcXIiOiBJbnRlcnF1YW50aWxlIFJhbmdlOwogICAgICAKICAgICAgKyAicnNkIjogUmVsYXRpdmUgU3RhbmRhcmQgRGV2aWF0aW9uOwogICAgICAKICAgICAgKyAibWFkIjogTWVkaWFuIEFic29sdXRlIERldmlhdGlvbjsKICAgICAgCiAgICAgICsgIm1lYW4iOiBNZWFuOwogICAgICAKICAgICAgKyAibWVkaWFuIjogTWVkaWFuLgogIAogICogX2J5LnBlcmNlbnRfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBhbW91bnQgb2YgdmFyaWFibGVzIHRvIGZpbHRlciBpbiB0aGUgZGF0YXNldCBpcyBnaXZlbiBpbiBhIHBlcmNlbnRhZ2UuIEl0IGRlZmF1bHRzIHRvIFRSVUU7CiAgCiAgKiBfYnkudGhyZXNob2xkXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgYW1vdW50IG9mIHZhcmlhYmxlcyB0byBmaWx0ZXIgaW4gdGhlIGRhdGFzZXQgaXMgZGVmaW5lZCBieSB3ZXRoZXIgdGhleSBhcmUgdW5kZXIgb3Igbm90IGEgZ2l2ZW4gdGhyZXNob2xkLiBJdCBkZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF9yZWQudmFsdWVfOiBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgYSBwZXJjZW50YWdlIG9yIHRocmVzaG9sZC4gSXQgY2FuIGFsc28gYmUgImF1dG8iLCBpbmRpY2F0aW5nIHRoYXQgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgdG8gcmVtb3ZlIGFyZSBjYWxjdWxhdGVkIGF1dG9tYXRpY2FsbHkuCgoKCiMjIFJlcGxhY2Ugc3BlY2lmaWMgZGF0YSBvciBtZXRhZGF0YSB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgpfX1lvdSBjYW4gcmVwbGFjZSBhIGRhdGEgdmFsdWUgZm9yIGEgbmV3IG9uZSBvbiB0aGUgZGF0YXNldF9fCgo+IHJlcGxhY2VfZGF0YV92YWx1ZShkYXRhc2V0LCB4LmF4aXMudmFsLCBzYW1wbGUsIG5ldy52YWx1ZSwgYnkuaW5kZXggPSBGKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3guYXhpcy52YWxfOiBpbmRleCBvciBuYW1lIG9mIHRoZSB2YXJpYWJsZSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBkYXRhIHBvaW50IHRvIHJlcGxhY2U7CiAgCiAgKiBfc2FtcGxlXzogbmFtZSBvZiB0aGUgc2FtcGxlIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIGRhdGEgcG9pbnQgdG8gcmVwbGFjZTsKICAKICAqIF9uZXcudmFsdWVfOiBuZXcgdmFsdWUgKG51bWVyaWMpIG9mIHRoZSBkYXRhIHBvaW50OwogIAogICogX2J5LmluZGV4XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgdmFsdWUgaW4gX3guYXhpcy52YWxfIGFyZ3VtZW50IGlzIGFuIGluZGV4IG9yIG5vdC4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCl9fWW91IGNhbiByZXBsYWNlIGEgbWV0YWRhdGEgdmFsdWUgZm9yIGEgbmV3IG9uZSBvbiB0aGUgZGF0YXNldF9fCgo+IHJlcGxhY2VfbWV0YWRhdGFfdmFsdWUoZGF0YXNldCwgdmFyaWFibGUsIHNhbXBsZSwgbmV3LnZhbHVlKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3ZhcmlhYmxlXzogbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbWV0YWRhdGEgcG9pbnQgdG8gcmVwbGFjZTsKICAKICAqIF9zYW1wbGVfOiBuYW1lIG9mIHRoZSBzYW1wbGUgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbWV0YWRhdGEgcG9pbnQgdG8gcmVwbGFjZTsKICAKICAqIF9uZXcudmFsdWVfOiBuZXcgdmFsdWUgKG51bWVyaWMgb3IgY2hhcmFjdGVyIHN0cmluZykgb2YgdGhlIG1ldGFkYXRhIHBvaW50LgoKCgoKCgojIFVuaXZhcmlhdGUgQW5hbHlzaXMKCiMjIFQtdGVzdAoKIyMjIEZ1bmN0aW9uIHRvIHVzZQoKPiB0VGVzdHNfZGF0YXNldChkYXRhc2V0LCBtZXRhZGF0YS52YXIsIHRocmVzaG9sZCA9IE5VTEwsIHdyaXRlLmZpbGUgPSBGLCBmaWxlLm91dCA9ICJ0dGVzdHMuY3N2IikgPGEgbmFtZT0idFRlc3RzX2RhdGFzZXQiPjwvYT4KCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9tZXRhZGF0YS52YXJfOiBtZXRhZGF0YSB2YXJpYWJsZSB0byB1c2UgaW4gdGhlIHQtdGVzdHMuIEl0IGlzIHRlc3RlZCB0aGUgZGlmZmVyZW5jZXMgaW4gbWVhbnMgb2YgdGhlIHZhcmlhYmxlcyBiZXR3ZWVuIHRoZSBzYW1wbGVzIHRoYXQgYmVsb25nIHRvIHRoZSBkaWZmZXJlbnQgZ3JvdXBzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBnaXZlbjsKICAKICAqIF90aHJlc2hvbGRfOiB0aHJlc2hvbGQgdmFsdWUgb2YgdGhlIHAtdmFsdWUuIE9ubHkgdGhlIHJlc3VsdHMgb2YgdGhlIHZhcmlhYmxlcyB3aG9zZSBwLXZhbHVlcyBhcmUgdW5kZXIgdGhlIHRocmVzaG9sZCBhcmUgcmV0dXJuZWQ7IF9fb3B0aW9uYWxfXwogIAogICogX3dyaXRlLmZpbGVfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHlvdSB3YW50IHRoZSByZXN1bHRzIHRvIGJlIHdyaXR0ZW4gaW4gYSBmaWxlLiBEZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF9maWxlLm91dF86IHN0cmluZyB3aXRoIHRoZSBmaWxlIHBhdGggdG8gd3JpdGUgdG8uIFlvdSBvbmx5IG5lZWQgdG8gZ2l2ZSB0aGlzIGluZm9ybWF0aW9uIGlmIHlvdSBjaG9zZW4gdG8gd3JpdGUgdGhlIHJlc3VsdHMgdG8gYSBmaWxlICh3cml0ZS5maWxlPVQpLiBEZWZhdWx0cyB0byAidHRlc3RzLmNzdiIuCgo+IHBsb3RfdHRlc3RzKGRhdGFzZXQsIHR0LnJlc3VsdHMsIHR0LnRocmVzaG9sZCA9IDAuMDEpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcmVzdWx0cyBvZiB0aGUgdC10ZXN0cyBnaXZlbiBpbiBfdHQucmVzdWx0c18gYXJndW1lbnQ7CiAgCiAgKiBfdHQucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIHQtdGVzdHMgcmVzdWx0cywgb2JhdGluZWQgZnJvbSBbdFRlc3RzX2RhdGFzZXRdKCN0VGVzdHNfZGF0YXNldCkgZnVuY3Rpb247CiAgCiAgKiBfdHQudHJlc2hvbGRfOiBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgdGhlIHAtdmFsdWUgdHJlc2hvbGQuIEFuIGhvcml6b250YWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgcC12YWx1ZXMgYXJlIHVuZGVyIHRoZSB0cmVzaG9sZCB3aWxsIGJlIGJsdWUsIHdoaWxlIHRoZSBvdGhlciBvbmVzIHdpbGwgYmUgZ3JleS4gRGVmYXVsdHMgdG8gMC4wMS4KICAKICAKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBjb25jZW50cmF0aW9ucyBkYXRhc2V0IChbY29uY2VudHJhdGlvbnMgZGF0YXNldCB1c2VkXSgjcmVhZF9jb25jZW50cmF0aW9uc19leCkpLgoKX18xLl9fIFBlcmZvcm0gdC10ZXN0cyBvbiB0aGUgdmFyaWFibGVzIG9mIHRoZSBkYXRhc2V0LCB0ZXN0aW5nIGlmIHRoZXkgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGJldHdlZW4gdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIF9NdXNjbGUubG9zc18KCmBgYHtyfQpyZXNfdHRlc3RfY29uY2VudHJhdGlvbnMgPSB0VGVzdHNfZGF0YXNldChjb25jZW50cmF0aW9uc19kYXRhc2V0LCAiTXVzY2xlLmxvc3MiKQpgYGAKCl9fMi5fXyBUYWJsZSB3aXRoIHRoZSByZXN1bHRzIG9idGFpbmVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3R0ZXN0X2NvbmNlbnRyYXRpb25zKQpgYGAKCl9fMy5fXyBQbG90IG9mIHRoZSByZXN1bHRzLCB3aXRoIGEgcC12YWx1ZSB0cmVzaG9sZCBvZiAwLjA1OgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBsb3RfdHRlc3RzKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIHJlc190dGVzdF9jb25jZW50cmF0aW9ucywgdHQudGhyZXNob2xkID0gMC4wNSkKYGBgCgoKIyMgT25lLVdheSBBTk9WQQoKIyMjIEZ1bmN0aW9uIHRvIHVzZQoKPiBhb3ZfYWxsX3ZhcnMoZGF0YXNldCwgY29sdW1uLmNsYXNzLCBkb1R1a2V5ID0gVCwgd3JpdGUuZmlsZSA9IEYsIGZpbGUub3V0ID0gImFub3ZhLXJlcy5jc3YiKSA8YSBuYW1lPSJhb3ZfYWxsX3ZhcnMiPjwvYT4KCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9jb2x1bW4uY2xhc3NfOiBtZXRhZGF0YSB2YXJpYWJsZSB0byB1c2UgaW4gdGhlIEFOT1ZBLiBJdCBpcyB0ZXN0ZWQgdGhlIGRpZmZlcmVuY2VzIGluIG1lYW5zIG9mIHRoZSB2YXJpYWJsZXMgYmV0d2VlbiB0aGUgc2FtcGxlcyB0aGF0IGJlbG9uZyB0byB0aGUgZGlmZmVyZW50IGdyb3VwcyBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgZ2l2ZW47CiAgCiAgKiBfZG9UdWtleV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgVHVrZXlIU0QgdGVzdCBzaG91bGQgYmUgcGVyZm9ybWVkIG9yIG5vdC4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF93cml0ZS5maWxlXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB5b3Ugd2FudCB0aGUgcmVzdWx0cyB0byBiZSB3cml0dGVuIGluIGEgZmlsZS4gRGVmYXVscyB0byBGQUxTRTsKICAKICAqIF9maWxlLm91dF86IHN0cmluZyB3aXRoIHRoZSBmaWxlIHBhdGggdG8gd3JpdGUgdG8uIFlvdSBvbmx5IG5lZWQgdG8gZ2l2ZSB0aGlzIGluZm9ybWF0aW9uIGlmIHlvdSBjaG9zZW4gdG8gd3JpdGUgdGhlIHJlc3VsdHMgdG8gYSBmaWxlICh3cml0ZS5maWxlPVQpLiBEZWZhdWx0cyB0byAiYW5vdmEtcmVzLmNzdiIuCgo+IHBsb3RfYW5vdmEoZGF0YXNldCwgYW5vdmEucmVzdWx0cywgYW5vdmEudGhyZXNob2xkID0gMC4wMSwgcmV2ZXJzZS54ID0gRikKCiAgKiBfZGF0YXNldF86IHRoZSBzcGVjbWluZSBkYXRhc2V0IHRoYXQgbGVkIHRvIHRoZSByZXN1bHRzIG9mIHRoZSB0LXRlc3RzIGdpdmVuIGluIF9hbm92YS5yZXN1bHRzXyBhcmd1bWVudDsKICAKICAqIF9hbm92YS5yZXN1bHRzXzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgb25lLXdheSBBTk9WQSByZXN1bHRzLCBvYmF0aW5lZCBmcm9tIFthb3ZfYWxsX3ZhcnNdKCNhb3ZfYWxsX3ZhcnMpIGZ1bmN0aW9uOwogIAogICogX2Fub3ZhLnRyZXNob2xkXzogbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIGhlIHAtdmFsdWUgdHJlc2hvbGQuIEFuIGhvcml6b250YWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgcC12YWx1ZXMgYXJlIHVuZGVyIHRoZSB0cmVzaG9sZCB3aWxsIGJlIGJsdWUsIHdoaWxlIHRoZSBvdGhlciBvbmVzIHdpbGwgYmUgZ3JleS4gRGVmYXVsdHMgdG8gMC4wMTsKICAKICAqIF9yZXZlcnNlLnhfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSB4LWF4aXMgc2hvdWxkIGJlIHBsb3R0ZWQgaW4gcmV2ZXJzZSBvciBub3QuIERlZmF1bHRzIHRvIEZBTFNFLgoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBJUiBkYXRhc2V0IChbSVIgZGF0YXNldCB1c2VkXSgjcmVhZF9pcl9leCkpLgoKX18xLl9fIFRvIHBlcmZvcm0gdGhpcyB0ZXN0LCB0aGUgb3JpZ2luYWwgZGF0YXNldCB3YXMgZmlsdGVyZWQsIHVzaW5nIGludGVycXVhbnRpbGUgcmFuZ2UgZnVuY3Rpb24gYW5kIGZpbHRlcmluZyA3NVwlIG9mIHRoZSB2YXJpYWJsZXM6CgpgYGB7cn0KaXJfc3BlY3RyYV9kYXRhc2V0X2ZwZj1mbGF0X3BhdHRlcm5fZmlsdGVyKGlyX3NwZWN0cmFfZGF0YXNldCwgcmVkLnZhbHVlPTc1KQpgYGAKCl9fMi5fXyBQZXJmb3JtIG9uZS13YXkgQU5PVkEgdGVzdHMgb24gdGhlIHZhcmlhYmxlcyBvZiB0aGUgZGF0YXNldCwgdGVzdGluZyBpZiB0aGV5IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBiZXR3ZWVuIHRoZSBjbGFzc2VzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBfdmFyaWV0aWVzXywgd2l0aG91dCBwZXJmb3JtaW5nIHRoZSBUdWtleUhTRCB0ZXN0OgoKYGBge3J9CnJlc19vbmVfYW5vdmFfaXIgPSBhb3ZfYWxsX3ZhcnMoaXJfc3BlY3RyYV9kYXRhc2V0X2ZwZiwgInZhcmlldGllcyIsIGRvVHVrZXkgPSBGQUxTRSkKYGBgCgpfXzMuX18gVGFibGUgd2l0aCB0aGUgcmVzdWx0cyBvYnRhaW5lZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlc19vbmVfYW5vdmFfaXIpCmBgYAoKX180Ll9fIFBsb3Qgb2YgdGhlIHJlc3VsdHMsIHdpdGggYSB0cmVzaG9sZCBvZiAwLjA1KjEwXi04OgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBsb3RfYW5vdmEoaXJfc3BlY3RyYV9kYXRhc2V0X2ZwZiwgcmVzX29uZV9hbm92YV9pciwgMC4wNWUtOCkKYGBgCgoKIyMgTXVsdGlmYWN0b3IgQU5PVkEKCiMjIyBGdW5jdGlvbnMgdG8gdXNlCgpfX1RvIHBlcmZvcm0gbXVsdGlmYWN0b3IgQU5PVkE6X18KCj4gbXVsdGlmYWN0b3JfYW92X2FsbF92YXJzKGRhdGFzZXQsIG1ldGFkYXRhLnZhcnMsIGNvbWJpbmF0aW9uKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGFkYXRhLnZhcnNfOiBjaGFyYWN0ZXIgdmVjdG9yIHdpdGggc3RyaW5ncyByZXByZXNlbnRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlcyB0byB1c2UgaW4gbXVsdGlmYWN0b3IgQU5PVkEuIEl0IGlzIHRlc3RlZCB0aGUgZGlmZmVyZW5jZXMgaW4gbWVhbnMgb2YgdGhlIHZhcmlhYmxlcyBiZXR3ZWVuIHRoZSBzYW1wbGVzIHRoYXQgYmVsb25nIHRvIHRoZSBkaWZmZXJlbnQgZ3JvdXBzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZXMgZ2l2ZW47CiAgCiAgKiBfY29tYmluYXRpb25fOiBzdHJpbmcgcmVwcmVzZW50YXRpbmcgYSBmb3JtdWxhIHNwZWNpZnlpbmcgdGhlIG1vZGVsLiBGb3IgZXhhbXBsZSwgaWYgX21ldGFkYXRhLnZhcnNfIGlzIGMoInZhcjEiLCAidmFyMiIpLCBfY29tYmluYXRpb25fIGNvdWxkIGJlICJ2YXIxK3ZhcjIiLgogIApfX1RvIGdldCB0aGUgcC12YWx1ZXMgdGFibGUgZnJvbSB0aGUgcmVzdWx0cyBvYnRhaW5lZCBieSB1c2luZyBwcmV2aW91cyBmdW5jdGlvbl9fCgo+IG11bHRpZmFjdG9yX2Fvdl9wdmFsdWVzX3RhYmxlKG11bHRpZmFjdG9yLmFvdi5yZXN1bHRzLCB3cml0ZS5maWxlID0gRiwgZmlsZS5vdXQgPSAibXVsdGktYW5vdmEtcHZhbHVlcy5jc3YiKQoKICAqIF9tdWx0aWZhY3Rvci5hb3YucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIG11bHRpZmFjdG9yIGFub3ZhIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gdXNpbmcgdGhlIHByZXZpb3VzIGZ1bmN0aW9uOwogIAogICogX3dyaXRlLmZpbGVfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHlvdSB3YW50IHRoZSByZXN1bHRzIHRhYmxlIHRvIGJlIHdyaXR0ZW4gaW4gYSBmaWxlLiBEZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF9maWxlLm91dF86IHN0cmluZyB3aXRoIHRoZSBmaWxlIHBhdGggdG8gd3JpdGUgdG8uIFlvdSBvbmx5IG5lZWQgdG8gZ2l2ZSB0aGlzIGluZm9ybWF0aW9uIGlmIHlvdSBjaG9zZW4gdG8gd3JpdGUgdGhlIHJlc3VsdHMgdG8gYSBmaWxlICh3cml0ZS5maWxlPVQpLiBEZWZhdWx0cyB0byAibXVsdGktYW5vdmEtcHZhbHVlcy5jc3YiLgogIApfX1RvIGdldCB0aGUgdGFibGUgd2l0aCB0aGUgZXhwbGFpbmVkIHZhcmlhYmlsaXR5IGZyb20gdGhlIHJlc3VsdHMgb2J0YWluZWQgYnkgdXNpbmcgdGhlIGZ1bmN0aW9uIF9tdWx0aWZhY3RvclxfYW92XF9hbGxcX3ZhcnNfIF9fCgo+IG11bHRpZmFjdG9yX2Fvdl92YXJleHBfdGFibGUobXVsdGlmYWN0b3IuYW92LnJlc3VsdHMsIHdyaXRlLmZpbGUgPSBGLCBmaWxlLm91dCA9ICJtdWx0aS1hbm92YS12YXJleHAuY3N2IikKCiAgKiBfbXVsdGlmYWN0b3IuYW92LnJlc3VsdHNfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBtdWx0aWZhY3RvciBhbm92YSByZXN1bHRzLCBvYnRhaW5lZCBmcm9tIHVzaW5nIHRoZSBwcmV2aW91cyBmdW5jdGlvbjsKICAKICAqIF93cml0ZS5maWxlXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB5b3Ugd2FudCB0aGUgcmVzdWx0cyB0YWJsZSB0byBiZSB3cml0dGVuIGluIGEgZmlsZS4gRGVmYXVsdHMgdG8gRkFsU0U7CiAgCiAgKiBfZmlsZS5vdXRfOiBzdHJpbmcgd2l0aCB0aGUgZmlsZSBwYXRoIHRvIHdyaXRlIHRvLiBZb3Ugb25seSBuZWVkIHRvIGdpdmUgdGhpcyBpbmZvcm1hdGlvbiBpZiB5b3UgY2hvc2VuIHRvIHdyaXRlIHRoZSByZXN1bHRzIHRvIGEgZmlsZSAod3JpdGUuZmlsZT1UKS4gRGVmYXVsdHMgdG8gIm11bHRpLWFub3ZhLXZhcmV4cC5jc3YiLgoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBubXItcGVha3MgZGF0YXNldCAoW05NUiBwZWFrcyBkYXRhc2V0IHVzZWRdKCNyZWFkX25tcl9wZWFrc19leCkpLgoKX18xLl9fIEFzIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGNvbnRhaW5zIG1pc3NpbmcgdmFsdWVzLCB5b3UgbXVzdCB0cmVhdCB0aGUgbWlzc2luZyB2YWx1ZXMsIGluIG9yZGVyIHRvIGJlIGFibGUgdG8gcGVyZm9ybSB0aGUgYW5hbHlzaXMuIFRoZSBtaXNzaW5nIHZhbHVlcyB3ZXJlIHJlcGxhY2VkIGJ5IHRoZSB2YWx1ZSBfMC4wMDAwNV86CgpgYGB7cn0Kbm1yX3BlYWtzX2RhdGFzZXRfbXY9bWlzc2luZ3ZhbHVlc19pbXB1dGF0aW9uKG5tcl9wZWFrc19kYXRhc2V0KQpgYGAKCl9fMi5fXyBQZXJmb3JtIG11bHRpZmFjdG9yIEFOT1ZBIHRlc3RzIG9uIHRoZSB2YXJpYWJsZXMgX3NlYXNvbnNfIGFuZCBfYWdyb3JlZ2lvbnNfIG9mIHRoZSBkYXRhc2V0LCBieSB0ZXN0aW5nIGlmIHRoZXkgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IHdoaXRoIHRoZSBtb2RlbCAic2Vhc29ucyphZ3JvcmVnaW9ucyI6CgpgYGB7cn0KcmVzX211bHRpX2Fub3ZhX25tcl9wZWFrcyA9IG11bHRpZmFjdG9yX2Fvdl9hbGxfdmFycyhubXJfcGVha3NfZGF0YXNldF9tdiwgYygic2Vhc29ucyIsImFncm9yZWdpb25zIiksICJzZWFzb25zKmFncm9yZWdpb25zIikKYGBgCgpfXzMuX18gVGFibGUgd2l0aCB0aGUgcC12YWx1ZXMgb2YgdGhlIHJlc3VsdHM6CgpgYGB7cn0KcmVzX211bHRpX2Fub3ZhX25tcl9wZWFrc19wLnZhbHVlcz1tdWx0aWZhY3Rvcl9hb3ZfcHZhbHVlc190YWJsZShyZXNfbXVsdGlfYW5vdmFfbm1yX3BlYWtzKQpEVDo6ZGF0YXRhYmxlKHJlc19tdWx0aV9hbm92YV9ubXJfcGVha3NfcC52YWx1ZXMpCmBgYAoKX180Ll9fIFRhYmxlIHdpdGggdGhlIGV4cGxhaW5lZCB2YXJpYWJpbGl0eSBvZiB0aGUgcmVzdWx0czoKCmBgYHtyfQpyZXNfbXVsdGlfYW5vdmFfbm1yX3BlYWtzX2V4cC52YXI9bXVsdGlmYWN0b3JfYW92X3ZhcmV4cF90YWJsZShyZXNfbXVsdGlfYW5vdmFfbm1yX3BlYWtzKQpEVDo6ZGF0YXRhYmxlKHJlc19tdWx0aV9hbm92YV9ubXJfcGVha3NfZXhwLnZhcikKYGBgCgoKIyMgS3J1c2thbC1XYWxsaXMgVGVzdAoKIyMjIEZ1bmN0aW9uIHRvIHVzZQoKPiBrcnVza2FsVGVzdF9kYXRhc2V0KGRhdGFzZXQsIG1ldGFkYXRhLnZhciwgdGhyZXNob2xkID0gTlVMTCwgd3JpdGUuZmlsZSA9IEYsIGZpbGUub3V0ID0gImtydXNrYWwuY3N2IikgPGEgbmFtZT0ia3J1c2thbFRlc3RfZGF0YXNldCI+PC9hPgoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGFkYXRhLnZhcl86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIHRvIHVzZSBpbiBLcnVza2FsLXdhbGxpcyB0ZXN0cy4gSXQgaXMgdGVzdGVkIHRoZSBkaWZmZXJlbmNlcyBpbiBtZWFucyBvZiB0aGUgdmFyaWFibGVzIGJldHdlZW4gdGhlIHNhbXBsZXMgdGhhdCBiZWxvbmcgdG8gdGhlIGRpZmZlcmVudCBncm91cHMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIGdpdmVuOwogIAogICogX3RyZXNob2xkXzogdGhyZXNob2xkIHZhbHVlIG9mIHRoZSBwLXZhbHVlLiBPbmx5IHRoZSByZXN1bHRzIG9mIHRoZSB2YXJpYWJsZXMgd2hvc2UgcC12YWx1ZXMgYXJlIHVuZGVyIHRoZSB0aHJlc2hvbGQgYXJlIHJldHVybmVkOyBfX29wdGlvbmFsX18KICAKICAqIF93cml0ZS5maWxlXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB5b3Ugd2FudCB0aGUgcmVzdWx0cyB0byBiZSB3cml0dGVuIGluIGEgZmlsZS4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfZmlsZS5vdXRfOiBzdHJpbmcgd2l0aCB0aGUgZmlsZSBwYXRoIHRvIHdyaXRlIHRvLiBZb3Ugb25seSBuZWVkIHRvIGdpdmUgdGhpcyBpbmZvcm1hdGlvbiBpZiB5b3UgY2hvc2VuIHRvIHdyaXRlIHRoZSByZXN1bHRzIHRvIGEgZmlsZSAod3JpdGUuZmlsZT1UKS4gRGVmYXVsdHMgdG8gImtydXNrYWwuY3N2Ii4KCj4gcGxvdF9rcnVza2FsdGVzdChkYXRhc2V0LCBrci5yZXN1bHRzLCBrci50aHJlc2hvbGQgPSAwLjAxKQoKICAqIF9kYXRhc2V0XzogdGhlIHNwZWNtaW5lIGRhdGFzZXQgdGhhdCBsZWQgdG8gdGhlIHJlc3VsdHMgb2YgdGhlIGtydXNrYWxsLXdhbGxpcyB0ZXN0cyBnaXZlbiBpbiBfa3IucmVzdWx0c18gYXJndW1lbnQ7CiAgCiAgKiBfa3IucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGtydXNrYWwtd2FsbGlzIHRlc3RzIHJlc3VsdCwgb2JhdGluZWQgZnJvbSBba3J1c2thbFRlc3RfZGF0YXNldF0oI2tydXNrYWxUZXN0X2RhdGFzZXQpIGZ1bmN0aW9uOwogIAogICogX2tyLnRyZXNob2xkXzogbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIGhlIHAtdmFsdWUgdHJlc2hvbGQuIEFuIGhvcml6b250YWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgcC12YWx1ZXMgYXJlIHVuZGVyIHRoZSB0cmVzaG9sZCB3aWxsIGJlIGJsdWUsIHdoaWxlIHRoZSBvdGhlciBvbmVzIHdpbGwgYmUgZ3JleS4gRGVmYXVsdHMgdG8gMC4wMS4KCiMjIyBFeGFtcGxlcwoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMS5fXyBQZXJmb3JtIEtydXNrYWwtV2FsbGlzIHRlc3RzIG9uIHRoZSB2YXJpYWJsZXMgb2YgdGhlIGRhdGFzZXQsIHRlc3RpbmcgaWYgdGhleSBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgY2xhc3NlcyBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgX011c2NsZS5sb3NzXy4gTm8gcC12YWx1ZSB0cmVzaG9sZCB3aWxsIGJlIGdpdmVuIHNvIHRoYXQgdGhlIGZ1bGwgcmVzdWx0cyBjYW4gYmUgc2VlbjoKCmBgYHtyfQpyZXNfa3d0ZXN0X2NvbmNlbnRyYXRpb25zID0ga3J1c2thbFRlc3RfZGF0YXNldChjb25jZW50cmF0aW9uc19kYXRhc2V0LCAiTXVzY2xlLmxvc3MiKQpgYGAKCl9fMi5fXyBUYWJsZSB3aXRoIHRoZSByZXN1bHRzIG9idGFpbmVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX2t3dGVzdF9jb25jZW50cmF0aW9ucykKYGBgCgpfXzMuX18gUGxvdCBvZiB0aGUgcmVzdWx0cywgd2l0aCBhIHRyZXNob2xkIHZhbHVlIG9mIDAuMDU6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGxvdF9rcnVza2FsdGVzdChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfa3d0ZXN0X2NvbmNlbnRyYXRpb25zLCBrci50aHJlc2hvbGQgPSAwLjA1KQpgYGAKCgojIyBLb2xtb2dvcm92IFNtaXJub3YgVGVzdAoKIyMjIEZ1bmN0aW9uIHRvIHVzZQoKPiBrc1Rlc3RfZGF0YXNldChkYXRhc2V0LCBtZXRhZGF0YS52YXIsIHRocmVzaG9sZCA9IE5VTEwsIHdyaXRlLmZpbGUgPSBGLCBmaWxlLm91dCA9ICJrcy5jc3YiKSA8YSBuYW1lPSJrc1Rlc3RfZGF0YXNldCI+PC9hPgoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGFkYXRhLnZhcl86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIHRvIHVzZSBpbiBLb2xtb2dvcm92LVNtaXJub3YgdGVzdHMuIEl0IGlzIHRlc3RlZCB0aGUgZGlmZmVyZW5jZXMgaW4gbWVhbnMgb2YgdGhlIHZhcmlhYmxlcyBiZXR3ZWVuIHRoZSBzYW1wbGVzIHRoYXQgYmVsb25nIHRvIHRoZSBkaWZmZXJlbnQgZ3JvdXBzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBnaXZlbjsKICAKICAqIF90cmVzaG9sZF86IHRocmVzaG9sZCB2YWx1ZSBvZiB0aGUgcC12YWx1ZS4gT25seSB0aGUgcmVzdWx0cyBvZiB0aGUgdmFyaWFibGVzIHdob3NlIHAtdmFsdWVzIGFyZSB1bmRlciB0aGUgdGhyZXNob2xkIGFyZSByZXR1cm5lZDsgX19vcHRpb25hbF9fCiAgCiAgKiBfd3JpdGUuZmlsZV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgeW91IHdhbnQgdGhlIHJlc3VsdHMgdG8gYmUgd3JpdHRlbiBpbiBhIGZpbGUuIERlZmF1bHRzIHRvIEZBTFNFOwogIAogICogX2ZpbGUub3V0Xzogc3RyaW5nIHdpdGggdGhlIGZpbGUgcGF0aCB0byB3cml0ZSB0by4gWW91IG9ubHkgbmVlZCB0byBnaXZlIHRoaXMgaW5mb3JtYXRpb24gaWYgeW91IGNob3NlbiB0byB3cml0ZSB0aGUgcmVzdWx0cyB0byBhIGZpbGUgKHdyaXRlLmZpbGU9VCkuIERlZmF1bHRzIHRvICJrcy5jc3YiLgoKPiBwbG90X2tzdGVzdChkYXRhc2V0LCBrcy5yZXN1bHRzLCBrcy50aHJlc2hvbGQgPSAwLjAxKQoKICAqIF9kYXRhc2V0XzogdGhlIHNwZWNtaW5lIGRhdGFzZXQgdGhhdCBsZWQgdG8gdGhlIHJlc3VsdHMgb2YgdGhlIGtvbG1vZ29yb3Ytc21pcm5vdiB0ZXN0cyBnaXZlbiBpbiBfa3MucmVzdWx0c18gYXJndW1lbnQ7CiAgCiAgKiBfa3MucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGtvbG1vZ29yb3Ytc21pcm5vdiB0ZXN0cyByZXN1bHQsIG9iYXRpbmVkIGZyb20gW2tzVGVzdF9kYXRhc2V0XSgja3NUZXN0X2RhdGFzZXQpIGZ1bmN0aW9uOwogIAogICogX2tzLnRyZXNob2xkXzogbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIGhlIHAtdmFsdWUgdHJlc2hvbGQuIEFuIGhvcml6b250YWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgcC12YWx1ZXMgYXJlIHVuZGVyIHRoZSB0cmVzaG9sZCB3aWxsIGJlIGJsdWUsIHdoaWxlIHRoZSBvdGhlciBvbmVzIHdpbGwgYmUgZ3JleS4gRGVmYXVsdHMgdG8gMC4wMS4KCiMjIyBFeGFtcGxlcwoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMS5fXyBQZXJmb3JtIEtvbG1vZ29yb3YtU21pcm5vdiB0ZXN0cyBvbiB0aGUgdmFyaWFibGVzIG9mIHRoZSBkYXRhc2V0LCB0ZXN0aW5nIGlmIHRoZXkgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGJldHdlZW4gdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIF9NdXNjbGUubG9zc18uIE5vIHAtdmFsdWUgdHJlc2hvbGQgd2lsbCBiZSBnaXZlbiBzbyB0aGF0IHRoZSBmdWxsIHJlc3VsdHMgY2FuIGJlIHNlZW46CgpgYGB7cn0KcmVzX2tzdGVzdF9jb25jZW50cmF0aW9ucyA9IGtzVGVzdF9kYXRhc2V0KGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsICJNdXNjbGUubG9zcyIpCmBgYAoKX18yLl9fIFRhYmxlIHdpdGggdGhlIHJlc3VsdHMgb2J0YWluZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShyZXNfa3N0ZXN0X2NvbmNlbnRyYXRpb25zKQpgYGAKCl9fMy5fXyBQbG90IG9mIHRoZSByZXN1bHRzLCB3aXRoIGEgcC12YWx1ZSB0cmVzaG9sZCBvZiAwLjA1OgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBsb3Rfa3N0ZXN0KGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIHJlc19rc3Rlc3RfY29uY2VudHJhdGlvbnMsIDAuMDUpCmBgYAoKCiMjIEZvbGQgQ2hhbmdlIEFuYWx5c2lzCgojIyMgRnVuY3Rpb25zIHRvIHVzZQoKX19Zb3UgY2FuIHBlcmZvcm0gZm9sZCBjaGFuZ2UgaW4gZWFjaCB2YXJpYWJsZSBvZiB0aGUgZGF0YXNldCwgaS5lLiwgYW5hbHlzZSB0aGUgZGlmZmVyZW5jZSBvZiBlYWNoIHZhcmlhYmxlIG9uIHR3byBncm91cHNfXwoKPiBmb2xkX2NoYW5nZShkYXRhc2V0LCBtZXRhZGF0YS52YXIsIHJlZi52YWx1ZSwgdGhyZXNob2xkLm1pbi5mYyA9IE5VTEwsIHdyaXRlLmZpbGUgPSBGLCBmaWxlLm91dCA9ICJmb2xkX2NoYW5nZS5jc3YiKSA8YSBuYW1lPSJmb2xkX2NoYW5nZSI+PC9hPgoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGFkYXRhLnZhcl86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIHRvIHVzZSBpbiB0aGUgZm9sZCBjaGFuZ2UgYW5hbHlzaXMuIEl0IGlzIHRlc3RlZCB0aGUgZGlmZmVyZW5jZXMgb2YgdGhlIHZhcmlhYmxlcyBvbiB0aGUgY2hvc2VuIG1ldGFkYXRhIHZhcmlhYmxlIGNsYXNzZXN0OwogIAogICogX3JlZi52YWx1ZV86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG5hbWUgb2Ygb25lIG9mIHRoZSBjbGFzc2VzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBjaG9zZW4gdGhhdCB3aWxsIGJlIGNvbnNpZGVyZWQgdGhlIGluaXRpYWwgdmFsdWU7CiAgCiAgKiBfdHJlc2hvbGQubWluLmZjXzogbWluaW11bSB0cmVzaG9sZCBvZiB0aGUgZm9sZCBjaGFuZ2UgdmFsdWUuIE9ubHkgdGhlIHJlc3VsdHMgb2YgdGhlIGdyb3VwcyB3aG9zZSBmb2xkIGNoYW5nZSB2YWx1ZXMgYXJlIGFib3ZlIHRoZSB0aHJlc2hvbGQgYXJlIHJldHVybmVkOyBfX29wdGlvbmFsX18KICAKICAqIF93cml0ZS5maWxlXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB5b3Ugd2FudCB0aGUgcmVzdWx0cyB0byBiZSB3cml0dGVuIGluIGEgZmlsZS4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfZmlsZS5vdXRfOiBzdHJpbmcgd2l0aCB0aGUgZmlsZSBwYXRoIHRvIHdyaXRlIHRvLiBZb3Ugb25seSBuZWVkIHRvIGdpdmUgdGhpcyBpbmZvcm1hdGlvbiBpZiB5b3UgY2hvc2VuIHRvIHdyaXRlIHRoZSByZXN1bHRzIHRvIGEgZmlsZSAod3JpdGUuZmlsZT1UKS4gRGVmYXVsdHMgdG8gImZvbGRfY2hhbmdlLmNzdiIuCgoKX19Zb3UgY2FuIHBlcmZvcm0gZm9sZCBjaGFuZ2Ugb24gdHdvIHZhcmlhYmxlcywgaS5lLiwgYW5hbHlzZSB0aGUgZGlmZmVyZW5jZSBvZiBncm91cHMgb24gdHdvIHZhcmlhYmxlc19fCgo+IGZvbGRfY2hhbmdlX3ZhcihkYXRhc2V0LCBtZXRhZGF0YS52YXIsIHZhcmlhYmxlcywgdGhyZXNob2xkLm1pbi5mYyA9IE5VTEwsIHdyaXRlLmZpbGUgPSBGLCBmaWxlLm91dCA9ICJmb2xkX2NoYW5nZV9yZXZlcnNlLmNzdiIpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0YWRhdGEudmFyXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgbWV0YWRhdGEgdmFyaWFibGUgdG8gdXNlIGluIHRoZSBmb2xkIGNoYW5nZSB0ZXN0LiBJdCBpcyB0ZXN0ZWQgdGhlIGRpZmZlcmVuY2VzIG9mIHRoZSBjbGFzc2VzIGluIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBvbiB0aGUgdHdvIHZhcmlhYmxlcyBjaG9zZW4gaW4gX3ZhcmlhYmxlc18gYXJndW1lbnQ7CiAgCiAgKiBfdmFyaWFibGVzXzogY2hhcmFjdGVyIHZlY3RvciB3aXRoIHRoZSBuYW1lcyByZXByZXNlbnRpbmcgdGhlIHR3byB2YXJpYWJsZXMgYmVpbmcgdGVzdGVkOwogIAogICogX3RyZXNob2xkLm1pbi5mY186IG1pbmltdW0gdHJlc2hvbGQgb2YgdGhlIGZvbGQgY2hhbmdlIHZhbHVlLiBPbmx5IHRoZSByZXN1bHRzIG9mIHRoZSBncm91cHMgd2hvc2UgZm9sZCBjaGFuZ2UgdmFsdWVzIGFyZSBhYm92ZSB0aGUgdGhyZXNob2xkIGFyZSByZXR1cm5lZDsgX19vcHRpb25hbF9fCiAgCiAgKiBfd3JpdGUuZmlsZV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgeW91IHdhbnQgdGhlIHJlc3VsdHMgdG8gYmUgd3JpdHRlbiBpbiBhIGZpbGUuIERlZmF1bHRzIHRvIEZBTFNFOwogIAogICogX2ZpbGUub3V0Xzogc3RyaW5nIHdpdGggdGhlIGZpbGUgcGF0aCB0byB3cml0ZSB0by4gWW91IG9ubHkgbmVlZCB0byBnaXZlIHRoaXMgaW5mb3JtYXRpb24gaWYgeW91IGNob3NlbiB0byB3cml0ZSB0aGUgcmVzdWx0cyB0byBhIGZpbGUgKHdyaXRlLmZpbGU9VCkuIERlZmF1bHRzIHRvICJmb2xkX2NoYW5nZV9yZXZlcnNlLmNzdiIuCgpfX1lvdSBjYW4gc2VlIGEgcGxvdCBvZiB0aGUgcmVzdWx0c19fCgo+IHBsb3RfZm9sZF9jaGFuZ2UoZGF0YXNldCwgZmMucmVzdWx0cywgZmMudGhyZXNob2xkLCBwbG90LmxvZyA9IFQsIHZhciA9IEYsIHhsYWIgPSAiIikKCiAgKiBfZGF0YXNldF86IHRoZSBzcGVjbWluZSBkYXRhc2V0IHRoYXQgbGVkIHRvIHRoZSByZXN1bHRzIG9mIHRoZSBmb2xkIGNoYW5nZSBhbmFseXNpcyBnaXZlbiBpbiBfZmMucmVzdWx0c18gYXJndW1lbnQ7CiAgCiAgKiBfZmMucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGZvbGQgY2hhbmdlIGFuYWx5c2lzIHJlc3VsdCwgb2JhdGluZWQgZnJvbSBbZm9sZF9jaGFuZ2VdKCNmb2xkX2NoYW5nZSkgZnVuY3Rpb247CiAgCiAgKiBfZmMudHJlc2hvbGRfOiBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgaGUgZm9sZCBjaGFuZ2UgdHJlc2hvbGQuIEFuIGhvcml6b250YWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgZm9sZCBjaGFuZ2UgdmFsdWVzIGFyZSB1bmRlciB0aGUgdHJlc2hvbGQgd2lsbCBiZSBibHVlLCB3aGlsZSB0aGUgb3RoZXIgb25lcyB3aWxsIGJlIGdyZXk7CiAgCiAgKiBfcGxvdC5sb2dfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHRoZSBmb2xkIGNoYW5nZSB2YWx1ZXMgYXJlIHRyYW5zZm9ybWVkIGxvZ2FyaXRobWljYWxseSBvciBub3QuIERlZmF1bHRzIHRvIFRSVUU7CiAgCiAgKiBfdmFyXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB0aGUgbGFiZWwgb2YgdGhlIHh4IGF4aXMgaXMgZ2l2ZW4gaW4gX3hsYWJfIGFyZ3VtZW50LiBEZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF94bGFiXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgeCBheGlzIGxhYmVsLiBfX29wdGlvbmFsX18KCl9fWW91IGNhbiBzZWUgYSBwbG90IHRoYXQgam9pbnMgdGhlIHJlc3VsdHMgZnJvbSBmb2xkIGNoYW5nZSBhbmFseXNpcyBhbmQgdC10ZXN0IGFuYWx5c2lzX18KCj4gdm9sY2Fub19wbG90X2ZjX3R0KGRhdGFzZXQsIGZjLnJlc3VsdHMsIHR0LnJlc3VsdHMsIGZjLnRocmVzaG9sZCA9IDIsIHR0LnRocmVzaG9sZCA9IDAuMDEpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcmVzdWx0cyBvZiB0aGUgZm9sZCBjaGFuZ2UgYW5hbHlzaXMgZ2l2ZW4gaW4gX2ZjLnJlc3VsdHNfIGFyZ3VtZW50IGFuZCB0aGUgdC10ZXN0IGFuYWx5c2lzIGdpdmVuIGluIF90dC5yZXN1bHRzXyBhcmd1bWVudDsKICAKICAqIF9mYy5yZXN1bHRzXzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgZm9sZCBjaGFuZ2UgYW5hbHlzaXMgcmVzdWx0LCBvYmF0aW5lZCBmcm9tIFtmb2xkX2NoYW5nZV0oI2ZvbGRfY2hhbmdlKSBmdW5jdGlvbjsKICAKICAqIF90dC5yZXN1bHRzXzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgdC10ZXN0IGFuYWx5c2lzIHJlc3VsdCwgb2JhdGluZWQgZnJvbSBbdFRlc3RzX2RhdGFzZXRdKCN0VGVzdHNfZGF0YXNldCkgZnVuY3Rpb247CiAgCiAgKiBfZmMudHJlc2hvbGRfOiBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgaGUgZm9sZCBjaGFuZ2UgdHJlc2hvbGQuIEEgdmVydGljYWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgZm9sZCBjaGFuZ2UgdmFsdWVzIGFyZSB1bmRlciB0aGUgdHJlc2hvbGQgd2lsbCBiZSBibHVlLCB3aGlsZSB0aGUgb3RoZXIgb25lcyB3aWxsIGJlIGdyZXk7CiAgCiAgKiBfdHQudHJlc2hvbGRfOiBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgaGUgZm9sZCBjaGFuZ2UgdHJlc2hvbGQuIEFuIGhvcml6b250YWwgbGluZSB3aWxsIGJlIGRyYXduIGluIHRoZSBwbG90IGFuZCB0aGUgZGF0YSBwb2ludHMgd2hvc2UgZm9sZCBjaGFuZ2UgdmFsdWVzIGFyZSB1bmRlciB0aGUgdHJlc2hvbGQgd2lsbCBiZSBibHVlLCB3aGlsZSB0aGUgb3RoZXIgb25lcyB3aWxsIGJlIGdyZXk7CgoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBjb25jZW50cmF0aW9ucyBkYXRhc2V0IChbY29uY2VudHJhdGlvbnMgZGF0YXNldCB1c2VkXSgjcmVhZF9jb25jZW50cmF0aW9uc19leCkpLgoKX18xLl9fIFBlcmZvcm0gZm9sZCBjaGFuZ2UgYW5hbHlzaXMgb24gZWFjaCB2YXJpYWJsZSBvZiB0aGUgZGF0YXNldCwgdGVzdGluZyB0aGUgZGlmZmVyZW5jZSBvZiBlYWNoIGRhdGEgdmFyaWFibGUgaW4gdGhlIHR3byBjbGFzc2VzIChjb250cm9sIGFuZCBjYWNoZXhpYykgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIF9NdXNjbGUubG9zc18uICJjb250cm9sIiB3YXMgY29uc2lkZXJlZCB0aGUgaW5pdGlhbCB2YWx1ZSBhbmQgbm8gdHJlc2hvbGQgd2FzIHNwZWNpZmllZDoKCmBgYHtyfQpyZXNfZmNfdmFyc19jb25jZW50cmF0aW9ucyA9IGZvbGRfY2hhbmdlKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsICJNdXNjbGUubG9zcyIsIHJlZi52YWx1ZT0iY29udHJvbCIpCmBgYAoKX18yLl9fIFRhYmxlIHdpdGggdGhlIHJlc3VsdHMgb2J0YWluZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShyZXNfZmNfdmFyc19jb25jZW50cmF0aW9ucykKYGBgCgpfXzMuX18gUGxvdCBvZiB0aGUgcmVzdWx0cywgd2l0aCBhIHRyZXNob2xkIHZhbHVlIG9mIDI6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGxvdF9mb2xkX2NoYW5nZShjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfZmNfdmFyc19jb25jZW50cmF0aW9ucywgMikKYGBgCgpfXzQuX18gUGxvdCBvZiB0aGUgZm9sZCBjaGFuZ2UgYW5kIHQtdGVzdCByZXN1bHRzIChmcm9tIHRoZSBleGFtcGxlIGZvciB0aGUgdC10ZXN0IGFuYWx5c2lzKSwgd2l0aCB0cmVzaG9sZCB2YWx1ZXMgb2YgMiBhbmQgMC4wMSByZXNwZWN0aXZlbHk6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0Kdm9sY2Fub19wbG90X2ZjX3R0KGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIHJlc19mY192YXJzX2NvbmNlbnRyYXRpb25zLCByZXNfdHRlc3RfY29uY2VudHJhdGlvbnMpCmBgYAoKX181Ll9fIFBlcmZvcm0gZm9sZCBjaGFuZ2UgYW5hbHlzaXMgb24gdGhlIGRpZmZlcmVuY2VzIG9mIHRoZSBjbGFzc2VzIG9mIHRoZSBfTXVzY2xlLmxvc3NfIG1ldGFkYXRhIHZhcmlhYmxlICgiY29udHJvbCIgYW5kImNhY2hleGljIikgb24gdGhlIHZhcmlhYmxlcyAiQ3JlYXRpbmUiIGFuZCAiU2VyaW5lIi4gQWdhaW4sIG5vIHRyZXNob2xkIHdhcyBzcGVjaWZpZWQ6CgpgYGB7cn0KcmVzX2ZjXzJ2YXJfY29uY2VudHJhdGlvbnMgPSBmb2xkX2NoYW5nZV92YXIoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgIk11c2NsZS5sb3NzIiwgYygiQ3JlYXRpbmUiLCAiU2VyaW5lIikpCmBgYAoKX182Ll9fIFRhYmxlIHdpdGggdGhlIHJlc3VsdHMgb2J0YWluZWQ6CgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShyZXNfZmNfMnZhcl9jb25jZW50cmF0aW9ucykKYGBgCgoKCgoKIyBQcmluY2lwYWwgQ29tcG9uZW50cyBBbmFseXNpcyAoUENBKQoKIyMgRnVuY3Rpb25zIHRvIHVzZQoKX19DbGFzc2ljYWwgUENBX18gPGEgbmFtZT0icGNhX2FuYWx5c2lzX2RhdGFzZXQiPjwvYT4KCj4gcGNhX2FuYWx5c2lzX2RhdGFzZXQoZGF0YXNldCwgc2NhbGUgPSBULCBjZW50ZXIgPSBULCB3cml0ZS5maWxlID0gRiwgZmlsZS5vdXQgPSAicGNhIikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9zY2FsZV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciB0aGUgdmFyaWFibGVzIHNob3VsZCBiZSBzY2FsZWQgb3Igbm90OwogIAogICogX2NlbnRlcl86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciB0aGUgdmFyaWFibGVzIHNob3VsZCBiZSBjZW50ZXJlZCBvciBub3Q7CiAgCiAgKiBfd3JpdGUuZmlsZV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgeW91IHdhbnQgdGhlIHJlc3VsdHMgdG8gYmUgd3JpdHRlbiBpbiBhIGZpbGUuIERlZmF1bHRzIHRvIEZBTFNFOwogIAogICogX2ZpbGUub3V0Xzogc3RyaW5nIHdpdGggdGhlIGZpbGUgcGF0aCB0byB3cml0ZSB0by4gWW91IG9ubHkgbmVlZCB0byBnaXZlIHRoaXMgaW5mb3JtYXRpb24gaWYgeW91IGNob3NlbiB0byB3cml0ZSB0aGUgcmVzdWx0cyB0byBhIGZpbGUgKHdyaXRlLmZpbGU9VCkuIERlZmF1bHRzIHRvICJwY2EiLgogIAoKX19Sb2J1c3QgUENBX18gPGEgbmFtZT0icGNhX3JvYnVzdCI+PC9hPgoKX1RoZSBkaWZmZXJlbmNlIHRvIHRoZSBwcmV2aW91cyBtZXRob2QgY29uc2lzdHMgb24gYmVpbmcgYWJsZSB0byBjaG9vc2UgaG93IHRvIGNlbnRlciBhbmQgc2NhbGUgdGhlIGRhdGFzZXRfCgo+IHBjYV9yb2J1c3QoZGF0YXNldCwgY2VudGVyID0gIm1lZGlhbiIsIHNjYWxlID0gIm1hZCIsIGsgPSAxMCwgd3JpdGUuZmlsZSA9IEYsIGZpbGUub3V0ID0gInJvYnBjYSIpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfY2VudGVyXzogc3RyaW5nIGluZGljYXRpbmcgaG93IHZhcmlhYmxlcyBzaG91bGQgYmUgY2VudGVyZWQuIEl0IGNhbiBlaXRoZXIgYmUgIm1lZGlhbiIgb3IgIm1lYW4iLiBJdCBjYW4gYWxzbyBiZSBhIG51bWVyaWMgdmVjdG9yIHdpdGggdGhlIGNlbnRlciB2YWx1ZXMgb2YgZWFjaCBjb2x1bW4gKHNhbXBsZSkuIERlZmF1bHRzIHRvICJtZWRpYW4iOwogIAogICogX3NjYWxlXzogc3RyaW5nIGluZGljYXRpbmcgaG93IHZhcmlhYmxlcyBzaG91bGQgYmUgc2NhbGVkLiBJdCBjYW4gZWl0aGVyIGJlIHN0YW5kYXJkIGRldmlhdGlvbiByYXRpbyAoInNkIikgb3IgbWVhbiBhYnNvbHV0ZSBkZXZpYXRpb24gKCJtYWQiKS4gSXQgY2FuIGFsc28gYmUgYSBudW1lcmljIHZlY3RvciB3aXRoIHRoZSBzY2FsZSB2YWx1ZSBvZiBlYWNoIGNvbHVtbiAoc2FtcGxlKS4gRGVmYXVsdHMgdG8gTWVhbiBhYnNvbHV0ZSBkZXZpYXRpb24gKCJtYWQiKTsKICAKICAqIF9rXzogbnVtYmVyIG9mIGNvbXBvbmVudHMgdG8gY29tcHV0ZTsKICAKICAqIF93cml0ZS5maWxlXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB5b3Ugd2FudCB0aGUgcmVzdWx0cyB0byBiZSB3cml0dGVuIGluIGEgZmlsZS4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfZmlsZS5vdXRfOiBzdHJpbmcgd2l0aCB0aGUgZmlsZSBwYXRoIHRvIHdyaXRlIHRvLiBZb3Ugb25seSBuZWVkIHRvIGdpdmUgdGhpcyBpbmZvcm1hdGlvbiBpZiB5b3UgY2hvc2VuIHRvIHdyaXRlIHRoZSByZXN1bHRzIHRvIGEgZmlsZSAod3JpdGUuZmlsZT1UKS4gRGVmYXVsdHMgdG8gInJvYnBjYSIuCgpfX051bWVyaWNhbCBSZXN1bHRzX18KCl9UaGUgZm9sbG93aW5nIGZ1bmN0aW9uIHJldHVybnMgdGhlIGltcG9ydGFuY2Ugb2YgdGhlIHByaW5jaXBhbCBjb21wb25lbnRzOl8KCj4gcGNhX2ltcG9ydGFuY2UocGNhLnJlcywgcGNzID0gMTpsZW5ndGgocGNhLnJlcyRzZGV2KSwgc2QgPSBULCBwcm9wID0gVCwgY3VtdWwgPSBULCBtaW4uY3VtID0gTlVMTCkKCiAgKiBfcGNhLnJlc186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIFBDQSByZXN1bHRzLCBvYnRhaW5lZCBmcm9tIFtwY2FfYW5hbHlzaXNfZGF0YXNldF0oI3BjYV9hbmFseXNpc19kYXRhc2V0KSBvciBbcGNhX3JvYnVzdF0oI3BjYV9yb2J1c3QpIGZ1bmN0aW9uOwogIAogICogX3Bjc186IG51bWVyaWMgdmVjdG9yIGluZGljYXRpbmcgZm9yIHdoYXQgcHJpbmNpcGFsIGNvbXBvbmVudHMgcGNhIGltcG9ydGFuY2Ugc2hvdWxkIGJlIGNhbGN1bGF0ZWQuIERlZmF1bHRzIHRvIGFsbCBjb21wb25lbnRzOwogIAogICogX3NkXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcmVzdWx0cyBzaG91bGQgYmUgcmV0dXJuZWQgb3Igbm90LiBEZWZhdWx0cyB0byBUUlVFOwogIAogICogX3Byb3BfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIHdoZXRoZXIgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBvZiB0aGUgcmVzdWx0cyBzaG91bGQgYmUgcmV0dXJuZWQgb3Igbm90LiBEZWZhdWxzIHRvIFRSVUU7CiAgCiAgKiBfY3VtdWxfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGN1bXVsYXRpdmUgdmFyaWFuY2Ugc2hvdWxkIGJlIHJldHVybmVkIG9yIG5vdC4gRGVmYXVsdHMgdG8gVFJVRTsKICAKICAqIF9taW4uY3VtXzogbnVtZXJpYyB2YWx1ZSwgaW4gcGVyY2VudGFnZSwgaW5kaWNhdGluZyB0aGUgbWluaW11bSBjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2YgdmFyaWFuY2UuIF9fb3B0aW9uYWxfXwoKCiMjIyBFeGFtcGxlcwoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMS5fXyBQZXJmb3JtIGEgbm9ybWFsIFBDQSBhbmFseXNpcywgYnkgc2NhbGluZyBhbmQgY2VudGVyaW5nIHRoZSB2YXJpYWJsZXM6CgpgYGB7cn0KcmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zID0gcGNhX2FuYWx5c2lzX2RhdGFzZXQoY29uY2VudHJhdGlvbnNfZGF0YXNldCkKYGBgCgpfXzIuX18gUENBIGltcG9ydGFuY2UgdGFibGU6CgpgYGB7cn0KcmVzX2ltcF9wY2FfY29uY2VudHJhdGlvbnM9cGNhX2ltcG9ydGFuY2UocmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zLCBwY3M9MTo1KQpEVDo6ZGF0YXRhYmxlKHJlc19pbXBfcGNhX2NvbmNlbnRyYXRpb25zLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWD1UKSkKYGBgCgpfXzMuX18gUENBIGxvYWRpbmdzIHRhYmxlOgoKX1RvIGdldCBsb2FkaW5ncyB2YWx1ZXMgb2Ygcm9idXN0IFBDQSByZXN1bHRzLCBpbnN0ZWFkIG9mICdyb3RhdGlvbicgY2hhbmdlIHRvICdsb2FkaW5ncycgXwoKYGBge3J9CkRUOjpkYXRhdGFibGUocm91bmQocmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zJHJvdGF0aW9uLCA0KSwgb3B0aW9ucz1saXN0KHNjcm9sbFg9VCkpCmBgYAoKX180Ll9fIFBDQSBzY29yZXMgdGFibGU6CgpfVG8gZ2V0IGxvYWRpbmdzIHZhbHVlcyBvZiByb2J1c3QgUENBIHJlc3VsdHMsIGluc3RlYWQgb2YgJ3gnIGNoYW5nZSB0byAnc2NvcmVzJyBfCgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShyb3VuZChyZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMkeCwgNCksIG9wdGlvbnM9bGlzdChzY3JvbGxYPVQpKQpgYGAKCl9fNS5fXyBUbyBzZWUgb3RoZXIgcmVzdWx0cyBhdmFpbGFibGUsIGdldCB0aGUgbmFtZXMgb2YgdGhlIHJlc3VsdCdzIGxpc3QgYW5kIHRoZW4gYWNjZXNzIHRoYXQgcmVzdWx0OgoKX0ZvciBtb3JlIGRldGFpbHMgb24gd2hhdCB0aGUgcmVzdWx0cyByZXR1cm4gc2VlIHRoZSB2YWx1ZSBzZWN0aW9uIG9mIGZ1bmN0aW9uIFtwcmNvbXBdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zdGF0cy92ZXJzaW9ucy8zLjQuMy90b3BpY3MvcHJjb21wKSBmb3Igbm9ybWFsIFBDQSByZXN1bHRzIGFuZCBbcHJpbmNvbXBdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zdGF0cy92ZXJzaW9ucy8zLjQuMy90b3BpY3MvcHJpbmNvbXApIGZvciByb2J1c3QgUENBIHJlc3VsdHMsIGZyb20gW3N0YXRzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy40LjMpIHBhY2thZ2UuCgpgYGB7cn0KbmFtZXMocmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zKQpyZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMkc2RldiAjU3RhbmRhcmQgZGV2aWF0aW9ucyBvZiB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMKYGBgCgoKIyMgUGxvdCBmdW5jdGlvbnMgYW5kIGV4YW1wbGVzCgpfRXhhbXBsZXMgZm9yIGVhY2ggcGxvdCB3aWxsIHVzZSB0aGUgcGNhIHJlc3VsdHMgZnJvbSB0aGUgZXhhbXBsZSBhYm92ZV8KCiMjIyBTY3JlZSBwbG90CgpfU2hvd3MgdGhlIGluZGl2aWR1YWwgYW5kIGN1bXVsYXRpdmUgcGVyY2VudGFnZXMgb2YgdGhlIGV4cGxhaW5lZCB2YXJpYW5jZSBvZiBlYWNoIHByaW5jaXBhbCBjb21wb25lbnRfCgo+IHBjYV9zY3JlZXBsb3QocGNhLnJlc3VsdCwgbnVtLnBjcyA9IE5VTEwsIGNleC5sZWcgPSAwLjgsIGxlZy5wb3MgPSAicmlnaHQiLCBsYWIudGV4dCA9IGMoImluZGl2aWR1YWwgcGVyY2VudCIsICJjdW11bGF0aXZlIHBlcmNlbnQiKSwgZmlsbC5jb2wgPSBjKCJibHVlIiwgInJlZCIpLCB5bGFiID0gIlBlcmNlbnRhZ2UiLCB4bGFiID0gIlByaW5jaXBhbCBjb21wb25lbnRzIiwgLi4uKQoKICAqIF9wY2EucmVzdWx0XzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgUENBIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gW3BjYV9hbmFseXNpc19kYXRhc2V0XSgjcGNhX2FuYWx5c2lzX2RhdGFzZXQpIG9yIFtwY2Ffcm9idXN0XSgjcGNhX3JvYnVzdCkgZnVuY3Rpb247CiAgCiAgKiBfbnVtLnBjc186IG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyB0byBzaG93IGluIHRoZSBwbG90LiBJZiBfTlVMTF8sIHNob3dzIGFsbCBjb21wb25lbnRzLiBEZWZhdWx0cyB0byBfTlVMTF87IAogIAogICogX2NleC5sZWdfOiBudW1iZXIgaW5kaWNhdGluZyB0aGUgcmVsYXRpdmUgZm9udCBzaXplIG9mIGxlZ2VuZC4gRGVmYXVsdHMgdG8gMC44OwogIAogICogX2xlZy5wb3NfOiBzdHJpbmcgaW5kaWNhdGluZyB0aGUgcG9zaXRpb24gb2YgdGhlIGxlZ2VuZCBpbiB0aGUgcGxvdC4gSXQgY2FuIGVpdGhlciBiZSAiYm90dG9tcmlnaHQiLCAiYm90dG9tIiwgImJvdHRvbWxlZnQiLCAibGVmdCIsICJ0b3BsZWZ0IiwgInRvcCIsICJ0b3ByaWdodCIsICJyaWdodCIgb3IgImNlbnRlciIuIERlZmF1bHRzIHRvICJyaWdodCI7CiAgCiAgKiBfbGFiLnRleHRfOiBjaGFyYWN0ZXIgdmVjdG9yIHdpdGggdGhlIGxhYmVscyBvZiB0aGUgbGVnZW5kLiBEZWZhdWx0cyB0byBjKCJpbmRpdmlkdWFsIHBlcmNlbnQiLCAiY3VtdWxhdGl2ZSBwZXJjZW50Iik7CiAgCiAgKiBfZmlsbC5jb2xfOiBjaGFyYWN0ZXIgdmVjdG9yIHdpdGggdGhlIGNvbG9ycyBvZiBlYWNoIGxpbmUuIERlZmF1bHRzIHRvIGMoImJsdWUiLCAicmVkIikuIE5hbWVzIG9mIHRoZSBhdmFpbGFibGUgY29sb3JzIGNhbiBiZSBjb25zdWx0ZWQgdXNpbmcgdGhlIGZ1bmN0aW9uIFtjb2xvcnNdXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZ3JEZXZpY2VzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9jb2xvcnMpIGZyb20gdGhlIFtnckRldmljZXNdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9nckRldmljZXMvdmVyc2lvbnMvMy40LjMpIHBhY2thZ2U7CiAgCiAgKiBfeWxhYl86IHl5IGF4aXMgbGFiZWwuIERlZmF1bHRzIHRvICJQZXJjZW50YWdlIjsKICAKICAqIF94bGFiXzogeHggYXhpcyBsYWJlbC4gRGVmYXVsdHMgdG8gIlByaW5jaXBhbCBjb21wb25lbnRzIjsKICAKICAqIF8uLi5fOiBhZGl0aW9uYWwgcGFyYW1ldGVycyBvZiBbbWF0cGxvdF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dyYXBoaWNzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9tYXRwbG90KSBmdW50aW9uLgoKIyMjIyBFeGFtcGxlcwoKX18xLl9fIFBsb3Qgc2hvd2luZyByZXN1bHRzIG9uIGFsbCBwcmluY2lwYWwgY29tcG9uZW50cywgd2hlcmUgdGhlIGJsdWUgbGluZSByZXByZXNlbnRzIHRoZSBpbmRpdmlkdWFsIHBlcmNlbnQgYW5kIHRoZSByZWQgb25lIHRoZSBjdW11bGF0aXZlIHBlcmNlbnQ6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGNhX3NjcmVlcGxvdChyZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMpCmBgYAoKCiMjIyBTY29yZXMgcGxvdCAyRAoKX1Nob3dzIGEgMkQgUENBIHNjb3JlcyBwbG90IG9mIHR3byBwcmluY2lwYWwgY29tcG9uZW50c18KCj4gcGNhX3Njb3Jlc3Bsb3QyRChkYXRhc2V0LCBwY2EucmVzdWx0LCBjb2x1bW4uY2xhc3MsIHBjYXMgPSBjKDEsIDIpLCBsYWJlbHMgPSBGQUxTRSwgZWxsaXBzZXMgPSBGQUxTRSwgYnc9RiwgcGFsbGV0dGUgPSAyLCBsZWcucG9zID0gInJpZ2h0IiwgeGxpbSA9IE5VTEwsIHlsaW0gPSBOVUxMKSA8YSBuYW1lPSJwY2Ffc2NvcmVzcGxvdDJEIj48L2E+CgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcGNhIHJlc3VsdHMgZ2l2ZW4gaW4gX3BjYS5yZXN1bHRfIGFyZ3VtZW50OwogIAogICogX3BjYS5yZXN1bHRfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBQQ0EgcmVzdWx0cywgb2J0YWluZWQgZnJvbSBbcGNhX2FuYWx5c2lzX2RhdGFzZXRdKCNwY2FfYW5hbHlzaXNfZGF0YXNldCkgb3IgW3BjYV9yb2J1c3RdKCNwY2Ffcm9idXN0KSBmdW5jdGlvbjsKICAKICAqIF9jb2x1bW4uY2xhc3NfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBuYW1lIG9mIGEgbWV0YWRhdGEgdmFyaWFibGUuIFRoZSBwbG90IGlzIGNvbG9yZWQgYnkgdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIG1lbnRpb25lZCBpbiB0aGlzIGFyZ3VtZW50OwogIAogICogX3BjYXNfOiBudW1lcmljIHZlY3RvciB3aXRoIHRoZSB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgdG8gcGxvdC4gRGVmYXVsdHMgdG8gYygxLDIpOwogIAogICogX2xhYmVsc186IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciBzYW1wbGVzJyBuYW1lcyBzaG91bGQgYmUgc2hvd24gaW4gdGhlIHBsb3QgKGVhY2ggcG9pbnQgaW4gdGhlIHBsb3QgY29ycmVzcG9uZHMgdG8gYSBzYW1wbGUpIG9yIG5vdC4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfZWxsaXBzZXNfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIHdoZXRoZXIgZWxsaXBzZXMgc2hvdWxkIGJlIGRyYXduIG9uIGVhY2ggY2xhc3Mgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIGNob3Nlbi4gRGVmYXVsdHMgdG8gRkFMU0UuIEVsbGlwc2VzIGFyZSBuZXZlciBkcmF3biB3aGVuIF9id18gYXJndW1lbnQgaXMgX1RSVUVfOwogIAogICogX2J3XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBwbG90IHNob3VsZCBibGFjayBhbmQgd2hpdGUgb3IgY29sb3JlZC4gSWYgYnc9VFJVRSwgdGhlIHBsb3QgaXMgYmxhY2sgYW5kIHdoaXRlLiBEZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF9wYWxsZXR0ZV86IHBhbGxldHRlIG9mIGNvbG9ycyBieSB3aGljaCB0byBjb2xvciB0aGUgcGxvdCAob25lIGRpZmZlcmVudCBjb2xvciB3aWxsIGJlIGFzc2lnbiB0byBlYWNoIG1ldGFkYXRhIGNsYXNzKS4gQ2FuIGJlIGEgbnVtYmVyIG9yIGEgc3RyaW5nIGluZGljYXRpbmcgdGhlIGNvbG9yIHBhbGV0dGUgd2FudGVkLiBFeGFtcGxlcyBvZiBjb2xvciBwYWxldHRlcyBhbmQgcmVzcGVjdGl2ZSBuYW1lcyAoZnJvbSBbUkNvbG9yQnJld2VyXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvUkNvbG9yQnJld2VyL3ZlcnNpb25zLzEuMS0yKSBwYWNrYWdlKToKICAKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzLCBmaWcuYWxpZ249ImNlbnRlciJ9ClJDb2xvckJyZXdlcjo6ZGlzcGxheS5icmV3ZXIuYWxsKCkKYGBgCgogICogX2xlZy5wb3NfOiBzdHJpbmcgaW5kaWNhdGluZyB0aGUgcG9zaXRpb24gb2YgdGhlIGxlZ2VuZCBpbiB0aGUgcGxvdC4gSXQgY2FuIGVpdGhlciBiZSAiYm90dG9tcmlnaHQiLCAiYm90dG9tIiwgImJvdHRvbWxlZnQiLCAibGVmdCIsICJ0b3BsZWZ0IiwgInRvcCIsICJ0b3ByaWdodCIsICJyaWdodCIgb3IgImNlbnRlciIuIERlZmF1bHRzIHRvICJyaWdodCI7CiAgCiAgKiBfeGxpbV86IG51bWVyaWMgdmVjdG9yIGluZGljYXRpbmcgdGhlIGxpbWl0cyBvZiB0aGUgeHggYXhpcy4gX19vcHRpb25hbF9fCiAgCiAgKiBfeWxpbV86IG51bWVyaWMgdmVjdG9yIGluZGljYXRpbmcgdGhlIGxpbWl0cyBvZiB0aGUgeXkgYXhpcy4gX19vcHRpb25hbF9fCgojIyMjIEV4YW1wbGVzCgpfXzEuX18gQ29sb3JlZCBwbG90IHNob3dpbmcgcmVzdWx0cyBvbiB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzLCB3aXRoIGVsbGlwc2VzIGRyYXduOgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBjYV9zY29yZXNwbG90MkQoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgcmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zLCAiTXVzY2xlLmxvc3MiLCBwY2FzID0gYygxLDIpLCBlbGxpcHNlcyA9IFRSVUUsIHBhbGxldHRlID0gIlBhaXJlZCIpCmBgYAoKX18yLl9fIFNhbWUgcGxvdCwgYnV0IGJsYWNrIGFuZCB3aGl0ZToKCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xMywgZmlnLmFsaWduPSJjZW50ZXIifQpwY2Ffc2NvcmVzcGxvdDJEKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIHJlc19ub3JtX3BjYV9jb25jZW50cmF0aW9ucywgIk11c2NsZS5sb3NzIiwgYnc9VFJVRSkKYGBgCgoKIyMjIE5vcm1hbCBTY29yZXMgcGxvdCAzRAoKPiBwY2Ffc2NvcmVzcGxvdDNEKGRhdGFzZXQsIHBjYS5yZXN1bHQsIGNvbHVtbi5jbGFzcywgcGNhcyA9IGMoMSwgMiwgMykpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcGNhIHJlc3VsdHMgZ2l2ZW4gaW4gX3BjYS5yZXN1bHRfIGFyZ3VtZW50OwogIAogICogX3BjYS5yZXN1bHRfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBQQ0EgcmVzdWx0cywgb2J0YWluZWQgZnJvbSBbcGNhX2FuYWx5c2lzX2RhdGFzZXRdKCNwY2FfYW5hbHlzaXNfZGF0YXNldCkgb3IgW3BjYV9yb2J1c3RdKCNwY2Ffcm9idXN0KSBmdW5jdGlvbjsKICAKICAqIF9jb2x1bW4uY2xhc3NfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBuYW1lIG9mIGEgbWV0YWRhdGEgdmFyaWFibGUuIFRoZSBwbG90IGlzIGNvbG9yZWQgYnkgdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIG1lbnRpb25lZCBpbiB0aGlzIGFyZ3VtZW50OwogIAogICogX3BjYXNfOiBudW1lcmljIHZlY3RvciB3aXRoIHRoZSB0aHJlZSBwcmluY2lwYWwgY29tcG9uZW50cyB0byBwbG90LiBEZWZhdWx0cyB0byBjKDEsMiwzKS4KCiMjIyMgRXhhbXBsZXMKCl9fMS5fXyBQbG90IHNob3dpbmcgcmVzdWx0cyBvbiB0aGUgZmlyc3QgdGhyZWUgcHJpbmNpcGFsIGNvbXBvbmVudHMsIGNvbG9yZWQgYnkgdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIF9NdXNjbGUubG9zc186CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGNhX3Njb3Jlc3Bsb3QzRChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMsICJNdXNjbGUubG9zcyIsIHBjYXMgPSBjKDEsMiwzKSkKYGBgCgoKIyMjIEludGVyYWN0aXZlIFNjb3JlcyBwbG90IDNECgpfVGhpcyBmdW5jdGlvbiB1c2VzIHRoZSBbcmdsXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcmdsL3ZlcnNpb25zLzAuOTkuMTYpIHBhY2thZ2UuIElmIGhhdmluZyBwcm9ibGVtcywgZ28gdG8gc2VjdGlvbiBbUHJvYmxlbXMgcnVubmluZyBjb2RlOiBzb2x1dGlvbnNdKCNwcm9ibGVtc19zb2x1dGlvbnMpXwoKPiBwY2Ffc2NvcmVzcGxvdDNEX3JnbChkYXRhc2V0LCBwY2EucmVzdWx0LCBjb2x1bW4uY2xhc3MsIHBjYXMgPSBjKDEsIDIsIDMpLCBzaXplID0gMSwgbGFiZWxzID0gRkFMU0UpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcGNhIHJlc3VsdHMgZ2l2ZW4gaW4gX3BjYS5yZXN1bHRfIGFyZ3VtZW50OwogIAogICogX3BjYS5yZXN1bHRfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBQQ0EgcmVzdWx0cywgb2J0YWluZWQgZnJvbSBbcGNhX2FuYWx5c2lzX2RhdGFzZXRdKCNwY2FfYW5hbHlzaXNfZGF0YXNldCkgb3IgW3BjYV9yb2J1c3RdKCNwY2Ffcm9idXN0KSBmdW5jdGlvbjsKICAKICAqIF9jb2x1bW4uY2xhc3NfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBuYW1lIG9mIGEgbWV0YWRhdGEgdmFyaWFibGUuIFRoZSBwbG90IGlzIGNvbG9yZWQgYnkgdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIG1lbnRpb25lZCBpbiB0aGlzIGFyZ3VtZW50OwogIAogICogX3BjYXNfOiBudW1lcmljIHZlY3RvciB3aXRoIHRoZSB0aHJlZSBwcmluY2lwYWwgY29tcG9uZW50cyB0byBwbG90LiBEZWZhdWx0cyB0byBjKDEsMiwzKTsKICAKICAqIF9zaXplXzogdmFsdWUgaW5kaWNhdGluZyB0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzLiBEZWZhdWx0cyB0byAxOwogIAogICogX2xhYmVsc186IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciBzYW1wbGVzJyBuYW1lcyBzaG91bGQgYmUgc2hvd24gaW4gdGhlIHBsb3QgKGVhY2ggcG9pbnQgaW4gdGhlIHBsb3QgY29ycmVzcG9uZHMgdG8gYSBzYW1wbGUpIG9yIG5vdC4gRGVmYXVsdHMgdG8gRkFMU0UuCgojIyMjIEV4YW1wbGVzCgpfXzEuX18gUGxvdCBzaG93aW5nIHJlc3VsdHMgb24gdGhlIGZpcnN0IDMgcHJpbmNpcGFsIGNvbXBvbmVudHMsIHdoZXJlIGVhY2ggc2FtcGxlIGlzIGxhYmVsZWQ6CgpfV2hlbiBydW5uaW5nIHRoaXMsIGEgcGxvdCBpbiBhbiBpbnRlcmFjdGl2ZSByZ2wgd2luZG93IGlzIHNob3duXwoKYGBge3IgZXZhbD1GfQpwY2Ffc2NvcmVzcGxvdDNEX3JnbChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMsICJNdXNjbGUubG9zcyIsIGxhYmVscyA9IFRSVUUpCmBgYAoKIAojIyMgQmlwbG90IDJECgo+IHBjYV9iaXBsb3QoZGF0YXNldCwgcGNhLnJlc3VsdCwgY2V4ID0gMC44LCBsZWdlbmQuY2V4ID0gMC44LCB4LmNvbG9ycyA9IDEsIGluc2V0ID0gYygwLCAwKSwgbGVnZW5kLnBsYWNlID0gInRvcHJpZ2h0IiwgLi4uKQoKICAqIF9kYXRhc2V0XzogdGhlIHNwZWNtaW5lIGRhdGFzZXQgdGhhdCBsZWQgdG8gdGhlIHBjYSByZXN1bHRzIGdpdmVuIGluIF9wY2EucmVzdWx0XyBhcmd1bWVudDsKICAKICAqIF9wY2EucmVzdWx0XzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgUENBIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gW3BjYV9hbmFseXNpc19kYXRhc2V0XSgjcGNhX2FuYWx5c2lzX2RhdGFzZXQpIG9yIFtwY2Ffcm9idXN0XSgjcGNhX3JvYnVzdCkgZnVuY3Rpb247CiAgCiAgKiBfY2V4XzogdmFsdWUgb2YgdGhlIHJlbGF0aXZlIGZvbnQgc2l6ZSBvZiBsZWdlbmQuIERlZmF1bHRzIHRvIDAuODsKICAKICAqIF9jb2xvcnNfOiAKICAKICAqIF9pbnNldF86IGFyZ3VtZW50IG9mIHRoZSBbbGVnZW5kXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZ3JhcGhpY3MvdmVyc2lvbnMvMy40LjMvdG9waWNzL2xlZ2VuZCkgZnVuY3Rpb24uCiAgCiAgKiBfbGVnZW5kLnBsYWNlXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIHBvc2l0aW9uIG9mIHRoZSBsZWdlbmQgaW4gdGhlIHBsb3QuIEl0IGNhbiBlaXRoZXIgYmUgImJvdHRvbXJpZ2h0IiwgImJvdHRvbSIsICJib3R0b21sZWZ0IiwgImxlZnQiLCAidG9wbGVmdCIsICJ0b3AiLCAidG9wcmlnaHQiLCAicmlnaHQiIG9yICJjZW50ZXIiLiBEZWZhdWx0cyB0byAidG9wcmlnaHQiOwogIAogICogXy4uLl86IGFkZGl0aW9uYWwgcGFyYW1ldGVycyBmb3IgW2JpcGxvdF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9iaXBsb3QpIGZ1bmN0aW9uLgoKIyMjIyBFeGFtcGxlcwoKX18xLl9fIEJpcGxvdCBvZiB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzLCBjb2xvcmVkIGJ5IHRoZSBjbGFzc2VzIGluIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBfTXVzY2xlLmxvc3NfOgoKYGBge3IgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTEzLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBjYV9iaXBsb3QoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgcmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zLCBjb2xvcnM9Ik11c2NsZS5sb3NzIikKYGBgCgoKIyMjIEludGVyYWN0aXZlIEJpcGxvdCAzRAoKX1RoaXMgZnVuY3Rpb24gdXNlcyB0aGUgW3JnbF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3JnbC92ZXJzaW9ucy8wLjk5LjE2KSBwYWNrYWdlLiBJZiBoYXZpbmcgcHJvYmxlbXMsIGdvIHRvIHNlY3Rpb24gW1Byb2JsZW1zIHJ1bm5pbmcgY29kZTogc29sdXRpb25zXSgjcHJvYmxlbXNfc29sdXRpb25zKV8KCj4gcGNhX2JpcGxvdDNEKGRhdGFzZXQsIHBjYS5yZXN1bHQsIGNvbHVtbi5jbGFzcyA9IE5VTEwsIHBjYXMgPSBjKDEsIDIsIDMpKQoKICAqIF9kYXRhc2V0XzogdGhlIHNwZWNtaW5lIGRhdGFzZXQgdGhhdCBsZWQgdG8gdGhlIHBjYSByZXN1bHRzIGdpdmVuIGluIF9wY2EucmVzdWx0XyBhcmd1bWVudDsKICAKICAqIF9wY2EucmVzdWx0XzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgUENBIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gW3BjYV9hbmFseXNpc19kYXRhc2V0XSgjcGNhX2FuYWx5c2lzX2RhdGFzZXQpIG9yIFtwY2Ffcm9idXN0XSgjcGNhX3JvYnVzdCkgZnVuY3Rpb247CiAgCiAgKiBfY29sdW1uLmNsYXNzXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgbmFtZSBvZiBhIG1ldGFkYXRhIHZhcmlhYmxlLiBUaGUgcGxvdCBpcyBjb2xvcmVkIGJ5IHRoZSBjbGFzc2VzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBtZW50aW9uZWQgaW4gdGhpcyBhcmd1bWVudDsgX19vcHRpb25hbF9fCiAgCiAgKiBfcGNhc186IG51bWVyaWMgdmVjdG9yIHdpdGggdGhlIHRocmVlIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHBsb3QuIERlZmF1bHRzIHRvIGMoMSwyLDMpLgoKCiMjIyMgRXhhbXBsZXMKCl9fMS5fXyBCaXBsb3Qgb2YgdGhlIGZpcnN0IHRocmVlIHByaW5jaXBhbCBjb21wb25lbnRzLCBjb2xvcmVkIGJ5IHRoZSBjbGFzc2VzIGluIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBfTXVzY2xlLmxvc3NfOgoKX1doZW4gcnVubmluZyB0aGlzLCBhIHBsb3QgaW4gYW4gaW50ZXJhY3RpdmUgcmdsIHdpbmRvdyBpcyBzaG93bl8KCmBgYHtyIGV2YWw9Rn0KcGNhX2JpcGxvdDNEKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIHJlc19ub3JtX3BjYV9jb25jZW50cmF0aW9ucywgIk11c2NsZS5sb3NzIiwgcGNhcyA9IGMoMSwyLDMpKQpgYGAKCiMjIyBQYWlycyBQbG90Cgo+IHBjYV9wYWlyc19wbG90KGRhdGFzZXQsIHBjYS5yZXN1bHQsIGNvbHVtbi5jbGFzcyA9IE5VTEwsIHBjYXMgPSBjKDEsIDIsIDMsIDQsIDUpLCAuLi4pCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcGNhIHJlc3VsdHMgZ2l2ZW4gaW4gX3BjYS5yZXN1bHRfIGFyZ3VtZW50OwogIAogICogX3BjYS5yZXN1bHRfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBQQ0EgcmVzdWx0cywgb2J0YWluZWQgZnJvbSBbcGNhX2FuYWx5c2lzX2RhdGFzZXRdKCNwY2FfYW5hbHlzaXNfZGF0YXNldCkgb3IgW3BjYV9yb2J1c3RdKCNwY2Ffcm9idXN0KSBmdW5jdGlvbjsKICAKICAqIF9jb2x1bW4uY2xhc3NfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBuYW1lIG9mIGEgbWV0YWRhdGEgdmFyaWFibGUuIFRoZSBwbG90IGlzIGNvbG9yZWQgYnkgdGhlIGNsYXNzZXMgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIG1lbnRpb25lZCBpbiB0aGlzIGFyZ3VtZW50OyBfX29wdGlvbmFsX18KICAKICAqIF9wY2FzXzogbnVtZXJpYyB2ZWN0b3Igd2l0aCB0aGUgdGhyZWUgcHJpbmNpcGFsIGNvbXBvbmVudHMgdG8gcGxvdC4gRGVmYXVsdHMgdG8gYygxLDIsMyk7CiAgCiAgKiBfLi4uXzogYWRkaXRpb25hbCBwYXJhbWV0ZXJzIHRvIFtnZ3BhaXJzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvR0dhbGx5L3ZlcnNpb25zLzEuMy4yL3RvcGljcy9nZ3BhaXJzKSBmdW5jdGlvbi4KCiMjIyMgRXhhbXBsZXMKCl9fMS5fXyBQYWlycyBwbG90IGZvciB0aGUgZmlyc3QgdGhyZWUgcHJpbmNpcGFsIGNvbXBvbmVudHMsIGNvbG9yZWQgYnkgdGhlIGNsYXNzZXMgaW4gdGhlIG1ldGFkYXRhIHZhcmlhYmxlIF9NdXNjbGUubG9zc186CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGNhX3BhaXJzX3Bsb3QoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgcmVzX25vcm1fcGNhX2NvbmNlbnRyYXRpb25zLCAiTXVzY2xlLmxvc3MiLCBwY2FzID0gYygxLDIsMykpCmBgYAoKCiMjIyBLbWVhbnMgUGxvdCAyRAoKPiBwY2Ffa21lYW5zX3Bsb3QyRChkYXRhc2V0LCBwY2EucmVzdWx0LCBudW0uY2x1c3RlcnMgPSAzLCBwY2FzID0gYygxLCAyKSwga21lYW5zLnJlc3VsdCA9IE5VTEwsIGxhYmVscyA9IEZBTFNFLCBidz1GLCBlbGxpcHNlcyA9IEZBTFNFLCBsZWcucG9zID0gInJpZ2h0IiwgeGxpbSA9IE5VTEwsIHlsaW0gPSBOVUxMKQoKICAqIF9kYXRhc2V0XzogdGhlIHNwZWNtaW5lIGRhdGFzZXQgdGhhdCBsZWQgdG8gdGhlIHBjYSByZXN1bHRzIGdpdmVuIGluIF9wY2EucmVzdWx0XyBhcmd1bWVudDsKICAKICAqIF9wY2EucmVzdWx0XzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgUENBIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gW3BjYV9hbmFseXNpc19kYXRhc2V0XSgjcGNhX2FuYWx5c2lzX2RhdGFzZXQpIG9yIFtwY2Ffcm9idXN0XSgjcGNhX3JvYnVzdCkgZnVuY3Rpb247CiAgCiAgKiBfbnVtLmNsdXN0ZXJzXzogdmFsdWUgaW5kaWNhdGluZyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIHRvIHNldCB0byB3aGVuIHBlcmZvcm1pbmcgdGhlIGstbWVhbnMuIE9ubHkgcmVsZXZhbnQgaWYga21lYW5zLnJlc3VsdD1OVUxMOwogIAogICogX3BjYXNfOiBudW1lcmljIHZlY3RvciB3aXRoIHRoZSB0aHJlZSBwcmluY2lwYWwgY29tcG9uZW50cyB0byBwbG90LiBEZWZhdWx0cyB0byBjKDEsMiwzKTsKICAKICAqIF9rbWVhbnMucmVzdWx0XzogdmFyaWFibGUgY29udGFpbmluZyBhIGstbWVhbnMgcmVzdWx0LiBJZiBub3QgZ2l2ZW4gKGttZWFucy5yZXN1bHQ9TlVMTCksIGstbWVhbnMgaXMgcGVyZm9ybWVkIGJ5IHRoZSBmdW5jdGlvbjsKICAKICAqIF9sYWJlbHNfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIHdoZXRoZXIgc2FtcGxlcycgbmFtZXMgc2hvdWxkIGJlIHNob3duIGluIHRoZSBwbG90IChlYWNoIHBvaW50IGluIHRoZSBwbG90IGNvcnJlc3BvbmRzIHRvIGEgc2FtcGxlKSBvciBub3QuIERlZmF1bHRzIHRvIEZBTFNFOwogIAogICogX2VsbGlwc2VzXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIGVsbGlwc2VzIHNob3VsZCBiZSBkcmF3biBvbiBlYWNoIGNsYXNzIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBjaG9zZW4uIERlZmF1bHRzIHRvIEZBTFNFLiBFbGxpcHNlcyBhcmUgbmV2ZXIgZHJhd24gd2hlbiBfYndfIGFyZ3VtZW50IGlzIF9UUlVFXzsKICAKICAqIF9id186IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgd2hldGhlciB0aGUgcGxvdCBzaG91bGQgYmxhY2sgYW5kIHdoaXRlIG9yIGNvbG9yZWQuIElmIGJ3PVRSVUUsIHRoZSBwbG90IGlzIGJsYWNrIGFuZCB3aGl0ZS4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfbGVnLnBvc186IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwb3NpdGlvbiBvZiB0aGUgbGVnZW5kIGluIHRoZSBwbG90LiBJdCBjYW4gZWl0aGVyIGJlICJib3R0b21yaWdodCIsICJib3R0b20iLCAiYm90dG9tbGVmdCIsICJsZWZ0IiwgInRvcGxlZnQiLCAidG9wIiwgInRvcHJpZ2h0IiwgInJpZ2h0IiBvciAiY2VudGVyIi4gRGVmYXVsdHMgdG8gInJpZ2h0IjsKICAKICAqIF94bGltXzogbnVtZXJpYyB2ZWN0b3IgaW5kaWNhdGluZyB0aGUgbGltaXRzIG9mIHRoZSB4eCBheGlzLiBfX29wdGlvbmFsX18KICAKICAqIF95bGltXzogbnVtZXJpYyB2ZWN0b3IgaW5kaWNhdGluZyB0aGUgbGltaXRzIG9mIHRoZSB5eSBheGlzLiBfX29wdGlvbmFsX18KCiMjIyMgRXhhbXBsZXMKCl9fMS5fXyBLbWVhbnMgcGxvdCBmb3IgdGhlIGZpcnN0IHR3byBjb21wb25lbnRzLCB3aGVyZSBhIGstbWVhbnMgb2YgdHdvIGNsdXN0ZXJzIGlzIHBlcmZvcm1lZDoKCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xMywgZmlnLmFsaWduPSJjZW50ZXIifQpwY2Ffa21lYW5zX3Bsb3QyRChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMsIG51bS5jbHVzdGVycyA9IDIpCmBgYAoKCiMjIyBLbWVhbnMgUGxvdCAzRAoKX1RoaXMgZnVuY3Rpb24gdXNlcyB0aGUgW3JnbF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3JnbC92ZXJzaW9ucy8wLjk5LjE2KSBwYWNrYWdlLiBJZiBoYXZpbmcgcHJvYmxlbXMsIGdvIHRvIHNlY3Rpb24gW1Byb2JsZW1zIHJ1bm5pbmcgY29kZTogc29sdXRpb25zXSgjcHJvYmxlbXNfc29sdXRpb25zKV8KCj4gcGNhX2ttZWFuc19wbG90M0QoZGF0YXNldCwgcGNhLnJlc3VsdCwgbnVtLmNsdXN0ZXJzID0gMywgcGNhcyA9IGMoMSwgMiwgMyksIGttZWFucy5yZXN1bHQgPSBOVUxMLCBsYWJlbHMgPSBGQUxTRSwgc2l6ZSA9IDEsIC4uLikKCiAgKiBfZGF0YXNldF86IHRoZSBzcGVjbWluZSBkYXRhc2V0IHRoYXQgbGVkIHRvIHRoZSBwY2EgcmVzdWx0cyBnaXZlbiBpbiBfcGNhLnJlc3VsdF8gYXJndW1lbnQ7CiAgCiAgKiBfcGNhLnJlc3VsdF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIFBDQSByZXN1bHRzLCBvYnRhaW5lZCBmcm9tIFtwY2FfYW5hbHlzaXNfZGF0YXNldF0oI3BjYV9hbmFseXNpc19kYXRhc2V0KSBvciBbcGNhX3JvYnVzdF0oI3BjYV9yb2J1c3QpIGZ1bmN0aW9uOwogIAogICogX251bS5jbHVzdGVyc186IHZhbHVlIGluZGljYXRpbmcgdGhlIG51bWJlciBvZiBjbHVzdGVycyB0byBzZXQgdG8gd2hlbiBwZXJmb3JtaW5nIHRoZSBrLW1lYW5zLiBPbmx5IHJlbGV2YW50IGlmIGttZWFucy5yZXN1bHQ9TlVMTDsKICAKICAqIF9wY2FzXzogbnVtZXJpYyB2ZWN0b3Igd2l0aCB0aGUgdGhyZWUgcHJpbmNpcGFsIGNvbXBvbmVudHMgdG8gcGxvdC4gRGVmYXVsdHMgdG8gYygxLDIsMyk7CiAgCiAgKiBfa21lYW5zLnJlc3VsdF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgYSBrLW1lYW5zIHJlc3VsdC4gSWYgbm90IGdpdmVuIChrbWVhbnMucmVzdWx0PU5VTEwpLCBrLW1lYW5zIGlzIHBlcmZvcm1lZCBieSB0aGUgZnVuY3Rpb247CiAgCiAgKiBfbGFiZWxzXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIHNhbXBsZXMnIG5hbWVzIHNob3VsZCBiZSBzaG93biBpbiB0aGUgcGxvdCAoZWFjaCBwb2ludCBpbiB0aGUgcGxvdCBjb3JyZXNwb25kcyB0byBhIHNhbXBsZSkgb3Igbm90LiBEZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF9zaXplXzogdmFsdWUgaW5kaWNhdGluZyB0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzLiBEZWZhdWx0cyB0byAxLgoKIyMjIyBFeGFtcGxlcwoKX18xLl9fIEttZWFucyBwbG90IGZvciB0aGUgZmlyc3QgdGhyZWUgY29tcG9uZW50cywgd2hlcmUgYSBrLW1lYW5zIG9mIHR3byBjbHVzdGVycyBpcyBwZXJmb3JtZWQ6CgpfV2hlbiBydW5uaW5nIHRoaXMsIGEgcGxvdCBpbiBhbiBpbnRlcmFjdGl2ZSByZ2wgd2luZG93IGlzIHNob3duXwoKYGBge3IgZXZhbD1GfQpwY2Ffa21lYW5zX3Bsb3QzRChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfbm9ybV9wY2FfY29uY2VudHJhdGlvbnMsIG51bS5jbHVzdGVycyA9IDIsIHBjYXMgPSBjKDEsMiwzKSkKYGBgCgoKIyMjIEttZWFucyBQYWlycyBQbG90Cgo+IHBjYV9wYWlyc19rbWVhbnNfcGxvdChkYXRhc2V0LCBwY2EucmVzdWx0LCBudW0uY2x1c3RlcnMgPSAzLCBrbWVhbnMucmVzdWx0ID0gTlVMTCwgcGNhcyA9IGMoMSwgMiwgMywgNCwgNSkpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcGNhIHJlc3VsdHMgZ2l2ZW4gaW4gX3BjYS5yZXN1bHRfIGFyZ3VtZW50OwogIAogICogX3BjYS5yZXN1bHRfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBQQ0EgcmVzdWx0cywgb2J0YWluZWQgZnJvbSBbcGNhX2FuYWx5c2lzX2RhdGFzZXRdKCNwY2FfYW5hbHlzaXNfZGF0YXNldCkgb3IgW3BjYV9yb2J1c3RdKCNwY2Ffcm9idXN0KSBmdW5jdGlvbjsKICAKICAqIF9udW0uY2x1c3RlcnNfOiB2YWx1ZSBpbmRpY2F0aW5nIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgdG8gc2V0IHRvIHdoZW4gcGVyZm9ybWluZyB0aGUgay1tZWFucy4gT25seSByZWxldmFudCBpZiBrbWVhbnMucmVzdWx0PU5VTEw7CiAgCiAgKiBfa21lYW5zLnJlc3VsdF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgYSBrLW1lYW5zIHJlc3VsdC4gSWYgbm90IGdpdmVuIChrbWVhbnMucmVzdWx0PU5VTEwpLCBrLW1lYW5zIGlzIHBlcmZvcm1lZCBieSB0aGUgZnVuY3Rpb247CiAgCiAgKiBfcGNhc186IG51bWVyaWMgdmVjdG9yIHdpdGggdGhlIHRocmVlIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHBsb3QuIERlZmF1bHRzIHRvIGMoMSwyLDMpLgoKIyMjIyBFeGFtcGxlcwoKX18xLl9fIEttZWFucyBwbG90IGZvciB0aGUgZmlyc3QgdGhyZWUgY29tcG9uZW50cywgd2hlcmUgYSBrLW1lYW5zIG9mIHR3byBjbHVzdGVycyBpcyBwZXJmb3JtZWQ6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGNhX3BhaXJzX2ttZWFuc19wbG90KGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIHJlc19ub3JtX3BjYV9jb25jZW50cmF0aW9ucywgbnVtLmNsdXN0ZXJzID0gMiwgcGNhcyA9IGMoMSwyLDMsNCw1KSkKYGBgCgoKCgoKIyBDbHVzdGVyaW5nIEFuYWx5c2lzCgojIyBGdW5jdGlvbiB0byB1c2UgPGEgbmFtZT0iY2x1c3RlcmluZyI+PC9hPgoKPiBjbHVzdGVyaW5nKGRhdGFzZXQsIG1ldGhvZCA9ICJoYyIsIGRpc3RhbmNlID0gImV1Y2xpZGVhbiIsIHR5cGUgPSAic2FtcGxlcyIsIG51bS5jbHVzdGVycyA9IDUsIGNsdXN0TWV0aG9kID0gImNvbXBsZXRlIikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9tZXRob2RfOiBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIGNsdXN0ZXJpbmcgbWV0aG9kLiBJdCBjYW4gZWl0aGVyIGJlOgogIAogICAgICArICJoYyI6IEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nLiBEZWZhdWx0IHZhbHVlOwogICAgICAKICAgICAgKyAia21lYW5zIjogSy1NZWFucyBDbHVzdGVyaW5nCiAgICAgIAogICogX2Rpc3RhbmNlXzogb25seSBuZWNlc3NhcnkgZm9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIGRpc3RhbmNlIG1lYXN1cmUgdG8gYmUgdXNlZCB3aGVuIGNvbXB1dGluZyB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdGhlIHNhbXBsZXMgb3IgdmFyaWFibGVzLiBJdCBjYW4gZWl0aGVyIGJlICJldWNsaWRlYW4iLCAibWFuaGF0dGFuIiwgInBlYXJzb24iIG9yICJzcGVhcm1hbiIuIERlZmF1bHRzIHRvICJldWNsaWRlYW4iOwogIAogICogX3R5cGVfOiBhIHN0cmluZyByZXByZXNlbnRpbmcgaWYgdGhlIGNsdXN0ZXIgYW5hbHlzaXMgc2hvdWxkIGJlIHBlcmZvcm1lZCBvbiBzYW1wbGVzICgic2FtcGxlcyIpIG9yIG9uIHZhcmlhYmxlcyAoInZhcmlhYmxlcyIpLiBEZWZhdWx0cyB0byAic2FtcGxlcyI7CiAgCiAgKiBfbnVtLmNsdXN0ZXJzXzogb25seSBuZWNlc3NhcnkgZm9yIGstbWVhbnMgY2x1c3RlcmluZywgYSBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgdGhlIG51bWJlciBvZiBjbHVzdGVycyBpbiB0aGUgay1tZWFucyBhbmFseXNpcy4gRGVmYXVsdHMgdG8gNTsKICAKICAqIF9jbHVzdE1ldGhvZF86IG9ubHkgbmVjZXNzYXJ5IGZvciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZywgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBhZ2dsb21lcmF0aW9uIG1ldGhvZC4gSXQgY2FuIGVpdGhlciBiZSAiY29tcGxldGUiLCAid2FyZCIsICJzaW5nbGUiLCAiYXZlcmFnZSIsICJtY3F1aXR0eSIsICJtZWRpYW4iIG9yICJjZW50cm9pZCIuIERlZmF1bHRzIHRvICJjb21wbGV0ZSIuCgojIyBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZwoKIyMjIFJlc3VsdHMgZnVuY3Rpb25zCgo+IGRlbmRyb2dyYW1fcGxvdF9jb2woZGF0YXNldCwgaGMucmVzdWx0LCBjbGFzc2VzLmNvbCwgY29sb3JzID0gTlVMTCwgdGl0bGUgPSAiIiwgbGFiLmNleCA9IDEsIGxlZy5wb3MgPSAidG9wcmlnaHQiLCBsYWJlbF9zYW1wbGVzPU5VTEwpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcmVzdWx0cyBvZiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBnaXZlbiBpbiBfaGMucmVzdWx0XyBhcmd1bWVudDsKICAKICAqIF9oYy5jbHVzdF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHJlc3VsdCwgb2J0YWluZWQgZnJvbSBbY2x1c3RlcmluZ10oI2NsdXN0ZXJpbmcpIGZ1bmN0aW9uOwogIAogICogX2NsYXNzZXMuY29sXzogc3RyaW5nIG9yIGluZGV4IHJlcHJlc2VudGluZyB0aGUgbWV0YWRhdGEgdmFyaWFibGUgYnkgd2hpY2ggdGhlIGxlYWZzIHdpbGwgYmUgY29sb3JlZDsKICAKICAqIF9jb2xvcnNfOiB2ZWN0b3Igd2l0aCB0aGUgZGlmZmVyZW50IGNvbG9ycyBmb3IgZWFjaCBtZXRhZGF0YSBjbGFzcyBpbiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgY2hvc2VuIGluIF9jbGFzc2VzLmNvbF8gYXJndW1lbnQuIElmIG5vdGhpbmcgaXMgZ2l2ZW4gKGNvbG9ycz1OVUxMKSwgdGhlIGNvbG9ycyB3aWxsIGJlIGdlbmVyYXRlZCBhdXRvbWF0aWNhbGx5LiBEZWZhdWx0cyB0byBOVUxMOwogIAogICogX3RpdGxlXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgdGl0bGUgdGhhdCBzaG91bGQgYXBwZWFyIGF0IHRoZSB0b3Agb2YgdGhlIHBsb3Q7IF9fb3B0aW9uYWxfXwogIAogICogX2xhYi5jZXhfOiBudW1lcmljIHZhbHVlIHJlcHJlc2VudGluZyB0aGUgcmVhbHRpdmUgc2l6ZSBvZiB0aGUgbGFiZWxzJyB0ZXh0LiBEZWZhdWx0cyB0byAxOwogIAogICogX2xlZy5wb3NfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBwb3NpdGlvbiBvZiB0aGUgbGVnZW5kIGluIHRoZSBwbG90LiAgSXQgY2FuIGVpdGhlciBiZSAiYm90dG9tcmlnaHQiLCAiYm90dG9tIiwgImJvdHRvbWxlZnQiLCAibGVmdCIsICJ0b3BsZWZ0IiwgInRvcCIsICJ0b3ByaWdodCIsICJyaWdodCIgb3IgImNlbnRlciIuIERlZmF1bHRzIHRvICJyaWdodCI7CiAgCiAgKiBfbGFiZWxfc2FtcGxlc186IHN0cmluZyBvciBpbmRleCByZXByZXNlbnRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIGJ5IHdoaWNoIHRoZSBsZWFmcyB3aWxsIGJlIG5hbWVkLiBJZiBub3QgZ2l2ZW4gKGxhYmVsX3NhbXBsZXM9TlVMTCksIHRoZSBsZWFmcyBhcmUgbmFtZWQgd2l0aCB0aGUgcmVzcGVjdGl2ZSBzYW1wbGVzIG5hbWVzLiBEZWZhdWx0cyB0byBOVUxMLgogIAogIAoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBjb25jZW50cmF0aW9ucyBkYXRhc2V0IChbY29uY2VudHJhdGlvbnMgZGF0YXNldCB1c2VkXSgjcmVhZF9jb25jZW50cmF0aW9uc19leCkpLgoKX18xLl9fIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9mIHRoZSBzYW1wbGVzLCB1c2luZyB0aGUgZXVjbGlkZWFuIGRpc3RhbmNlIGFuZCB0aGUgYWdnbG9tZXJhdGlvbiBtZXRob2QgY29tcGxldGU6CgpgYGB7cn0KcmVzX2hjbHVzdF9jb25jZW50cmF0aW9ucyA9IGNsdXN0ZXJpbmcoY29uY2VudHJhdGlvbnNfZGF0YXNldCkKYGBgCgpfXzIuX18gRGVuZG9ncmFtIHBsb3QsIHdoZXJlIHNhbXBsZXMgYXJlIGNvbG9yZWQgYnkgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIF9NdXNjbGUubG9zc186CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KZGVuZHJvZ3JhbV9wbG90X2NvbChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfaGNsdXN0X2NvbmNlbnRyYXRpb25zLCAiTXVzY2xlLmxvc3MiLCB0aXRsZSA9ICJEZW5kb2dyYW0gcGxvdCBmb3IgY29uY2VudHJhdGlvbnMgZGF0YXNldCIpCmBgYAoKX18zLl9fIFRoZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyByZXN1bHQgb2J0YWluZWQgaXMgYSBsaXN0IHdpdGggdGhlIGZvbGxvd2luZyBjb21wb25lbnRzOgoKX1RvIGtub3cgbW9yZSBhYm91dCB0aGUgcmV0dXJuZWQgcmVzdWx0cyBmb3IgYSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZywgc2VlIHRoZSBpbmZvcm1hdGlvbiBvbiB0aGUgZnVuY3Rpb24gW2hjbHVzdF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9oY2x1c3QpLl8KCmBgYHtyfQpuYW1lcyhyZXNfaGNsdXN0X2NvbmNlbnRyYXRpb25zKQpgYGAKCgoKIyMgSy1NZWFucyBDbHVzdGVyaW5nCgojIyMgUmVzdWx0cyBmdW5jdGlvbnMKCl9fWW91IGNhbiB2aXN1YWxpemUgYSBrLW1lYW5zIHBsb3QgZm9yIGVhY2ggY2x1c3Rlcl9fCgo+IGttZWFuc19wbG90KGRhdGFzZXQsIGttZWFucy5yZXN1bHQpCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgcmVzdWx0cyBvZiBrLW1lYW5zIGNsdXN0ZXJpbmcgZ2l2ZW4gaW4gX2hjLnJlc3VsdF8gYXJndW1lbnQ7CiAgCiAgKiBfa21lYW5zLnJlc3VsdF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGstbWVhbnMgY2x1c3RlcmluZyByZXN1bHQsIG9idGFpbmVkIGZyb20gW2NsdXN0ZXJpbmddKCNjbHVzdGVyaW5nKSBmdW5jdGlvbjsKICAKX19Zb3UgY2FuIHNlZSBhIGRhdGFmcmFtZSB3aXRoIHRoZSBzYW1wbGVzIHRoYXQgYmVsb25nIHRvIGVhY2ggY2x1c3Rlcl9fCgo+IGttZWFuc19yZXN1bHRfZGYoa21lYW5zLnJlc3VsdCkKCiAgKiBfa21lYW5zLnJlc3VsdF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGstbWVhbnMgY2x1c3RlcmluZyByZXN1bHQsIG9idGFpbmVkIGZyb20gW2NsdXN0ZXJpbmddKCNjbHVzdGVyaW5nKSBmdW5jdGlvbjsKICAKCiMjIyBFeGFtcGxlcwoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKS4KCl9fMS5fXyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBvZiB0aGUgc2FtcGxlcywgdXNpbmcgdGhlIGV1Y2xpZGVhbiBkaXN0YW5jZSBhbmQgdGhlIGFnZ2xvbWVyYXRpb24gbWV0aG9kIGNvbXBsZXRlOgoKYGBge3J9CnJlc19rY2x1c3RfY29uY2VudHJhdGlvbnMgPSBjbHVzdGVyaW5nKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIG1ldGhvZD0ia21lYW5zIiwgbnVtLmNsdXN0ZXJzPTIpCmBgYAoKX18yLl9fIEstbWVhbnMgcmVzdWx0cyBwbG90LCB3aGVyZSB0aGUgZ3JleSByZXByZXNlbnRzIHRoZSB2YWx1ZXMgb2YgYWxsIHNhbXBsZXMgb2YgdGhhdCBjbHVzdGVyIGFuZCBibHVlIHRoZSBtZWRpYW4gb2YgdGhvc2Ugc2FtcGxlczoKCmBgYHtyIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xMywgZmlnLmFsaWduPSJjZW50ZXIifQprbWVhbnNfcGxvdChjb25jZW50cmF0aW9uc19kYXRhc2V0LCByZXNfa2NsdXN0X2NvbmNlbnRyYXRpb25zKQpgYGAKCl9fMy5fXyBLLW1lYW5zIHJlc3VsdHMgZGF0YSBmcmFtZToKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKGttZWFuc19yZXN1bHRfZGYocmVzX2tjbHVzdF9jb25jZW50cmF0aW9ucyksIG9wdGlvbnM9bGlzdChzY3JvbGxYPVQpLCByb3duYW1lcz1GKQpgYGAKCl9fNC5fXyBUaGUgay1tZWFucyBjbHVzdGVyaW5nIHJlc3VsdCBvYnRhaW5lZCBpcyBhIGxpc3Qgd2l0aCB0aGUgZm9sbG93aW5nIGNvbXBvbmVudHM6CgpfVG8ga25vdyBtb3JlIGFib3V0IHRoZSByZXR1cm5lZCByZXN1bHRzIGZvciBhIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBzZWUgdGhlIGluZm9ybWF0aW9uIG9uIHRoZSBmdW5jdGlvbiBba21lYW5zXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy40LjMvdG9waWNzL2ttZWFucykuXwoKYGBge3J9Cm5hbWVzKHJlc19rY2x1c3RfY29uY2VudHJhdGlvbnMpCmBgYAoKCgoKCiMgTWFjaGluZSBMZWFybmluZwoKX01hY2hpbmUgbGVhcm5pbmcgaW4gdGhpcyBwYWNrYWdlIGlzIG9ubHkgYXZhaWxhYmxlIGZvciBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcy4gVHJ5IHRvIFtjb252ZXJ0IHRoZSBudW1lcmljIG1ldGFkYXRhIHRvIGZhY3RvcnNdKCNjb252ZXJ0X3RvX2ZhY3Rvcikgc28gdGhhdCB5b3UgY2FuIHVzZSB0aGVtIGZvciBtYWNoaW5lIGxlYXJuaW5nXwoKIyMgVHJhaW4gTW9kZWxzCgo+IHRyYWluX21vZGVsc19wZXJmb3JtYW5jZShkYXRhc2V0LCBtb2RlbHMsIGNvbHVtbi5jbGFzcywgdmFsaWRhdGlvbiwgbnVtLmZvbGRzID0gMTAsIG51bS5yZXBlYXRzID0gMTAsIHR1bmVsZW5ndGggPSAxMCwgdHVuZWdyaWQgPSBOVUxMLCBtZXRyaWMgPSBOVUxMLCBjb21wdXRlLnZhcmltcCA9IFQpIDxhIG5hbWU9InRyYWluX21vZGVsc19wZXJmb3JtYW5jZSI+PC9hPgoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21vZGVsc186IGNoYXJhY3RlciB2ZWN0b3IgaW5kaWNhdGluZyB0aGUgbW9kZWxzIHRvIHRyYWluLiBUaGUgbW9kZWxzIHRoYXQgY2FuIGJlIHVzZWQgYXJlIHRoZSBvbmVzIGF2YWlsYWJsZSBmb3IgdGhlIF9jYXJldF8gcGFja2FnZS4gQSBsaXN0IHdpdGggdGhlIGF2YWlsYWJsZSBtb2RlbHMgY2FuIGJlIGFjY2Vzc2VkIFtoZXJlXShodHRwczovL3RvcGVwby5naXRodWIuaW8vY2FyZXQvYXZhaWxhYmxlLW1vZGVscy5odG1sKS4gVGhlIG5hbWVzIHRvIHVzZSBpbiB0aGlzIGFyZ3VtZW50IHRoYXQgcmVwcmVzZW50IHRoZSBtb2RlbCB3YW50ZWQgYXJlIGluIHRoZSBjb2x1bW4gXyJtZXRob2QgVmFsdWUiXy4gT25seSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgY2FuIGJlIHVzZWQgKF8iVHlwZSJfIGNvbHVtbik7CiAgCiAgKiBfY29sdW1uLmNsYXNzXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgbWV0YWRhdGEgdmFyaWFibGUgdG8gcHJlZGljdDsKICAKICAqIF92YWxpZGF0aW9uXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgdmFsaWRhdGlvbiBtZXRob2QuIEl0IGNhbiBlaXRoZXIgYmU6CiAgCiAgICAgICsgImJvb3QiOiBSZXNhbXBsaW5nOwogICAgICAKICAgICAgKyAiY3YiOiBDcm9zcy1WYWxpZGF0aW9uOwogICAgICAKICAgICAgKyAicmVwZWF0ZWRjdiI6IFJlcGVhdGVkIENyb3NzLXZhbGlkYXRpb247CiAgICAgIAogICAgICArICJsb29jdiI6IExlYXZlIE9uZSBPdXQgQ3Jvc3MtVmFsaWRhdGlvbjsKICAgICAgCiAgICAgICsgImxnb2N2IjogTGVhdmUgR3JvdXAgT3V0IENyb3NzLVZhbGlkYXRpb24uCiAgICAgIAogICogX251bS5mb2xkc186IGlmIGEgY3Jvc3MgdmFsaWRhdGlvbiBtZXRob2QgaXMgY2hvc2VuLCBhIG51bWVyaWMgdmFsdWUgd2l0aCB0aGUgbnVtYmVyIG9mIGZvbGRzIG11c3QgYmUgZ2l2ZW4uIERlZmF1bHRzIHRvIDEwOwogIAogICogX251bS5yZXBlYXRzXzogaWYgImJvb3QiIG9yICJyZXBlYXRlZGN2IiBhcmUgY2hvc2VuLCBhIG51bWVyaWMgdmFsdWUgd2l0aCB0aGUgbnVtYmVyIG9mIHJlcGVhdHMgbXVzdCBiZSBnaXZlbi4gRGVmYXVsdHMgdG8gMTA7CiAgCiAgKiBfdHVuZWxlbmd0aF86IG51bWVyaWMgdmFsdWUgaW5kaWNhdGluZyB0aGUgbnVtYmVyIG9mIGRpZmZlcmVudCB2YWx1ZXMgZm9yIGVhY2ggcGFyYW1ldGVyIG9mIHRoZSBtb2RlbChzKSBjaG9zZW4gc2hvdWxkIGJlIHRlc3RlZCwgdG8gZmluZCB0aGUgYmVzdCBvbmUgYW1vbmcgdGhlbS4gRGVmYXVsdHMgdG8gMTA7CiAgCiAgKiBfdHVuZWdyaWRfOiBsaXN0IG9mIGRhdGFmcmFtZShzKSB3aXRoIHBvc3NpYmxlIHR1bmluZyB2YWx1ZXMgZm9yIGVhY2ggbW9kZWwgdG8gdHJhaW4uIFNlZSBleGFtcGxlcyBiZWxvdyBmb3IgbW9yZSBkZXRhaWxzIChfc3RlcCA4Xyk7CiAgCiAgKiBfbWV0cmljXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIG1ldHJpYyB0byB1c2Ugd2hlbiBldmFsdWF0aW5nIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlLiBJdCBjYW4gZWl0aGVyIGJlICJBY2N1cmFjeSIgb3IgIlJPQyI7CiAgCiAgKiBfY29tcHV0ZS52YXJpbXBfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIHdoZXRoZXIgdmFyaWFibGUgaW1wb3J0YW5jZSBzaG91bGQgYmUgY2FsY3VsYXRlZCAoY29tcHV0ZS52YXJpbXA9VFJVRSkgb3Igbm90IChjb21wdXRlLnZhcmltcD1GQUxTRSkuIERlZmF1bHRzIHRvIFRSVUUuCgpfXzNEIFBsb3QgZm9yIGEgZmluYWwgUExTIG1vZGVsIHRyYWluZWRfXwoKPiBwY2FfcGxvdF8zZChkYXRhc2V0LCBtb2RlbCwgdmFyLmNsYXNzLCBwY2FzID0gMTozLCBjb2xvcnMgPSBOVUxMLCBsZWdlbmQucGxhY2UgPSAidG9wcmlnaHQiLCAuLi4pCgogICogX2RhdGFzZXRfOiB0aGUgc3BlY21pbmUgZGF0YXNldCB0aGF0IGxlZCB0byB0aGUgZmluYWwgUExTIG1vZGVsIHRyYWluZWQgZ2l2ZW4gaW4gX21vZGVsXyBhcmd1bWVudDsKICAKICAqIF9tb2RlbF86IHZhcmlhYmxlIGNvbnRhaW5pbmcgYSBwbHMgZmluYWwgbW9kZWwsIG9idGFpbmVkIGZyb20gdGhlIHJlc3VsdHMgb2YgW3RyYWluX21vZGVsc19wZXJmb3JtYW5jZV0oI3RyYWluX21vZGVsc19wZXJmb3JtYW5jZSkgZnVuY3Rpb24sIGluICdyZXN1bHRzJGZpbmFsLm1vZGVsJHBscyc7CiAgCiAgKiBfdmFyLmNsYXNzXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgbmFtZSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUgYnkgd2hpY2ggdG8gY29sb3IgdGhlIHBsb3QgZGF0YSBwb2ludHM7CiAgCiAgKiBfcGNhc186IG51bWVyaWMgdmVjdG9yIHdpdGggdGhlIDMgY29tcG9uZW50cyB0byBwbG90LiBEZWZhdWx0cyB0byBjKDEsMiwzKSwgdGhlIGZpcnN0IHRocmVlIGNvbXBvbmVudHMgb2YgdGhlIG1vZGVsOwogIAogICogX2NvbG9yc186IGNoYXJhY3RlciB2ZWN0b3Igd2l0aCB0aGUgbmFtZXMgb2YgdGhlIGNvbG9ycyB0aGF0IHdpbGwgcmVwcmVzZW50IGVhY2ggY2xhc3Mgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlOwogIAogICogX2xlZ2VuZC5wbGFjZV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwb3NpdGlvbiBvZiB0aGUgbGVnZW5kIGluIHRoZSBwbG90LiBJdCBjYW4gZWl0aGVyIGJlICJib3R0b21yaWdodCIsICJib3R0b20iLCAiYm90dG9tbGVmdCIsICJsZWZ0IiwgInRvcGxlZnQiLCAidG9wIiwgInRvcHJpZ2h0IiwgInJpZ2h0IiBvciAiY2VudGVyIi4gRGVmYXVsdHMgdG8gInRvcHJpZ2h0IjsKICAKICAqIF8uLi5fOiBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgb2YgW2xlZ2VuZF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dyYXBoaWNzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9sZWdlbmQpIGZ1bmN0aW9uLgoKIyMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBjb25jZW50cmF0aW9ucyBkYXRhc2V0IChbY29uY2VudHJhdGlvbnMgZGF0YXNldCB1c2VkXSgjcmVhZF9jb25jZW50cmF0aW9uc19leCkpLgoKX18xLl9fIFRyYWluaW5nIG1vZGVsIFBMUyAoInBscyIpIGFuZCBSYW5kb20gRm9yZXN0cyAoInJmIikgdG8gcHJlZGljdCB0aGUgbWV0YWRhdGEgdmFyaWFibGUgX011c2NsZS5sb3NzXyAoaS5lLiwgaWYgdGhlIHNhbXBsZSBiZWxvbmdzIHRvIF9jYWNoZXhpY18gb3IgX2NvbnRyb2xfKS4gRm9yIG1vZGVsIHZhbGlkYXRpb24sIGl0IHdhcyBoZXJlIHVzZWQgdGhlIGNyb3NzIHZhbGlkYXRpb24gbWV0aG9kICgiY3YiKSwgd2l0aCAxMCBmb2xkcy4gRm9yIHBhcmFtZXRlciBvcHRpbWl6YXRpb24sIGl0IHdhcyBzZXQgdG8gdGVzdCAxMCBkaWZmZXJlbnQgdmFsdWVzIG9mIGVhY2ggcGFyYW1ldGVyIG9mIHRoZSB0d28gbW9kZWxzIGNob3NlbjoKCmBgYHtyfQpyZXNfdHJhaW5fY29uY2VudHJhdGlvbnM9dHJhaW5fbW9kZWxzX3BlcmZvcm1hbmNlKGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIGMoInBscyIsICJyZiIpLCAiTXVzY2xlLmxvc3MiLCAiY3YiLCBtZXRyaWM9IkFjY3VyYWN5IikKYGBgCgpfXzIuX18gVGFibGUgd2l0aCB0aGUgcGVyZm9ybWFuY2VzIG9mIGVhY2ggYmVzdCBtb2RlbCB0cmFpbmVkOgoKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3RyYWluX2NvbmNlbnRyYXRpb25zJHBlcmZvcm1hbmNlKQpgYGAKCl9fMy5fXyBUaGUgcGFyYW1ldGVycyBvZiBlYWNoIGJlc3QgbW9kZWwgdHJhaW5lZDoKCiAgKiBQTFMgbW9kZWwKICAKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3RyYWluX2NvbmNlbnRyYXRpb25zJGJlc3QudHVuZXMkcGxzKQpgYGAKCiAgKiBSYW5kb20gRm9yZXN0IG1vZGVsCiAgCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlc190cmFpbl9jb25jZW50cmF0aW9ucyRiZXN0LnR1bmVzJHJmKQpgYGAKCl9fNC5fXyBUYWJsZXMgd2l0aCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBmb3IgZWFjaCBtb2RlbDoKCiAgKiBQTFMgbW9kZWwKICAKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3RyYWluX2NvbmNlbnRyYXRpb25zJHZpcHMkcGxzKQpgYGAKCiAgKiBSYW5kb20gRm9yZXN0IG1vZGVsCiAgCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlc190cmFpbl9jb25jZW50cmF0aW9ucyR2aXBzJHJmKQpgYGAKCl9fNS5fXyBSZXN1bHRzIGZvciBlYWNoIHZhbHVlIG9mIHRoZSBwYXJhbWV0ZXJzIHRlc3RlZDoKCiAgKiBQTFMgbW9kZWwKICAKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3RyYWluX2NvbmNlbnRyYXRpb25zJGZ1bGwucmVzdWx0cyRwbHMpCmBgYAoKICAqIFJhbmRvbSBGb3Jlc3QgbW9kZWwKICAKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3RyYWluX2NvbmNlbnRyYXRpb25zJGZ1bGwucmVzdWx0cyRyZikKYGBgCgpfXzYuX18gQ29uZnVzaW9uIG1hdHJpY2VzIGZvciBlYWNoIG1vZGVsIHRyYWluZWQ6CgogICogUExTIG1vZGVsCiAgCmBgYHtyfQpyZXNfdHJhaW5fY29uY2VudHJhdGlvbnMkY29uZnVzaW9uLm1hdHJpY2VzJHBscwpgYGAKCiAgKiBSYW5kb20gRm9yZXN0IG1vZGVsCiAgCmBgYHtyfQpyZXNfdHJhaW5fY29uY2VudHJhdGlvbnMkY29uZnVzaW9uLm1hdHJpY2VzJHJmCmBgYAoKX183Ll9fIEVhY2ggZmluYWwgbW9kZWwgY2FuIGJlIGFjY2Vzc2VkIHRocm91Z2ggYHJlc190cmFpbl9jb25jZW50cmF0aW9ucyRmaW5hbC5tb2RlbHMkcGxzYCBmb3IgdGhlIFBMUyBtb2RlbCBhbmQgYHJlc190cmFpbl9jb25jZW50cmF0aW9ucyRmaW5hbC5tb2RlbHMkcmZgIGZvciB0aGUgUmFuZG9tIEZvcmVzdHMgbW9kZWwuIEl0IGlzIHdpdGggdGhlc2UgZmluYWwgbW9kZWxzIHRoYXQgdGhlIHVzZXIgY2FuIHByZWRpY3QgbmV3IHNhbXBsZXMuCgpfXzguX18gSWYgeW91IHdhbnQgdG8gcHJvdmlkZSBzcGVjaWZpYyBwYXJhbWV0ZXIgdmFsdWVzIHRvIHRlc3QsIHRoaXMgaXMgaG93IHlvdSB1c2UgdGhlIF90dW5ncmlkXyBhcmd1bWVudC4gWW91IHdpbGwgaGF2ZSB0byBjb25zdWx0IHRoZSBbZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly90b3BlcG8uZ2l0aHViLmlvL2NhcmV0L2F2YWlsYWJsZS1tb2RlbHMuaHRtbCkgaW4gY2FyZXQgcGFja2FnZSB0byBrbm93IHdoYXQgYXJlIHRoZSBwYXJhbWV0ZXJzIHRoYW4gY2FuIGJlIHRlc3RlZCBmb3IgZWFjaCBtb2RlbC4KCiogSGVyZSwgaXQgaXMgcHJlc2VudCBhbiBleGFtcGxlIGZvciB0aGUgbW9zdCBjb21tb25seSB1c2VkIG1vZGVscyBvbiBob3cgdG8gY3JlYXRlIHRoZSBkYXRhZnJhbWUgb2YgdHVuaW5nIHBhcmFtZXRlcnMgZm9yIGVhY2ggb25lIG9mIHRoZW0sIHVzaW5nIF9leHBhbmQuZ3JpZF8gZnVuY3Rpb246CgpgYGB7cn0KCiMjVkFMVUVTIEhFUkUgSU5UUk9EVUNFRCBGT1IgUEFSQU1FVEVSIE9QVElNSVpBVElPTiBBUkUgT05MWSBGT1IgRVhQTEFJTklORyBQVVJQT1NFUywgVEhFWSBNSUdIVCBOT1QgQkUgVEhFIEJFU1QgVkFMVUVTIEZPUiBZT1UgVE8gVVNFIElOIFRFU1RJTkcjIyMKCiMjRm9yIFBMUyBtb2RlbCAtICJwbHMiOgojIyMjI1RoaXMgbW9kZWxzIG9ubHkgaGFzIG9uZSBwYXJhbWV0ZXIgLSBuY29tcApuQ29tcD1jKDEsMiwzLDQsNSw2KSAjSGVyZSB3ZSBhcmUgc2F5aW5nIHRoYXQgd2Ugd2FudCB0byB0ZXN0IHRoZSB2YWx1ZXMgZnJvbSAxIHRvIDYgZm9yIHRoaXMgcGFyYW1ldGVyCnBsc190dW5pbmdWYWx1ZXNfZGF0YWZyYW1lPWV4cGFuZC5ncmlkKG5jb21wPW5Db21wKQoKIyNGb3IgREVDSVNJT04gVFJFRSAoQzQuNS1saWtlIFRyZWVzKSBtb2RlbCAtICJKNDgiOgojIyMjI1RoaXMgbW9kZWwgaGFzIHR3byBwYXJhbWV0ZXJzIC0gTSBhbmQgQwptPWMoMSwyLDMpCkM9YygxLDIsMyw0KQpKNDhfdHVuaW5nVmFsdWVzX2RhdGFmcmFtZT1leHBhbmQuZ3JpZChNPW0sIEM9QykKICAKIyNGb3IgUlVMRSBCQVNFRCBDTEFTU0lGSUVSIG1vZGVsIC0gIkpSaXAiOgojIyMjI1RoaXMgbW9kZWwgaGFzIHRoZXJlZSBwYXJhbWV0ZXJzIC0gTnVtT3B0cywgTnVtRm9sZHMgYW5kIE1pbldlaWdodHMKbnVtb3B0cz1jKDEsMiwzKQpudW1mb2xkcz1jKDEsMiwzKQptaW53ZWlnaHRzPWMoMSwyLDMpCkpSaXBfdHVuaW5nVmFsdWVzX2RhdGFmcmFtZT1leHBhbmQuZ3JpZChOdW1PcHQ9bnVtb3B0cywgTnVtRm9sZHM9bnVtZm9sZHMsIE1pbldlaWdodHM9bWlud2VpZ2h0cykKCiMjRm9yIFNWTSBMSU5FQVIgbW9kZWwgLSAic3ZtTGluZWFyIjoKIyMjIyNUaGlzIG1vZGVsIGhhcyBvbmUgcGFyYW1ldGVyIC0gY29zdApjb3N0PWMoMSw0LDYpCnN2bUxpbmVhcl90dW5pbmdWYWx1ZXNfZGF0YWZyYW1lPWV4cGFuZC5ncmlkKGNvc3Q9Y29zdCkKCiMjRm9yIFJBTkRPTSBGT1JFU1RTIG1vZGVsIC0gInJmIjoKIyMjIyNUaGlzIG1vZGVsIGhhcyBvbmUgcGFyYW1ldGVyIC0gbXRyeQptdHJ5PWMoNSw3LDgpCnJmX3R1bmluZ1ZhbHVlc19kYXRhZnJhbWU9ZXhwYW5kLmdyaWQobXRyeT1tdHJ5KQoKIyNGb3IgTkVVUkFMIE5FVFdPUktTIG1vZGVsIC0gIm5uZXQiOgojIyMjI1RoaXMgbW9kZWwgaGFzIHR3byBwYXJhbWV0ZXJzIC0gc2l6ZSBhbmQgZGVjYXkKc2l6ZT1jKDMsNCw1KQpkZWNheT1jKDEsMikKbm5ldF90dW5pbmdWYWx1ZXNfZGF0YWZyYW1lPWV4cGFuZC5ncmlkKHNpemU9c2l6ZSwgZGVjYXk9ZGVjYXkpCmBgYAoKCiogTm93LCBpZiB3YW50ZWQgdG8gdHJhaW4gUExTIGFuZCBSYW5kb20gRm9yZXN0cyBtb2RlbHMsIGxpa2UgdGhlIGV4YW1wbGUgYWJvdmUsIGJ1dCB1c2luZyBzcGVjaWZpYyBwYXJhbWV0ZXJzIHRvIHRlc3QsIHlvdSBzaG91bGQgcGVyZm9ybSBhcyBmb2xsb3dzOgoKYGBge3J9CiNDcmVhdGluZyB0aGUgbGlzdCBvZiBkYXRhZnJhbWVzIG9mIHR1bmluZyBwYXJhbWV0ZXJzCnR1bmVMaXN0PWxpc3QoKQp0dW5lTGlzdFtbInBscyJdXT1wbHNfdHVuaW5nVmFsdWVzX2RhdGFmcmFtZQp0dW5lTGlzdFtbInJmIl1dPXJmX3R1bmluZ1ZhbHVlc19kYXRhZnJhbWUKCnJlc190cmFpbl9jb25jZW50cmF0aW9ucz10cmFpbl9tb2RlbHNfcGVyZm9ybWFuY2UoY29uY2VudHJhdGlvbnNfZGF0YXNldCwgYygicGxzIiwgInJmIiksICJNdXNjbGUubG9zcyIsICJjdiIsIG1ldHJpYz0iQWNjdXJhY3kiLCB0dW5lZ3JpZD10dW5lTGlzdCkKYGBgCgoqIEFzIHlvdSBjYW4gc2VlLCBub3cgdGhlIHJlc3VsdHMgZm9yIGVhY2ggdmFsdWUgb2YgdGhlIHBhcmFtZXRlcnMgdGVzdGVkIGFyZToKICAKICBfUExTIG1vZGVsXwogIApgYGB7cn0KRFQ6OmRhdGF0YWJsZShyZXNfdHJhaW5fY29uY2VudHJhdGlvbnMkZnVsbC5yZXN1bHRzJHBscykKYGBgCgogIF9SYW5kb20gRm9yZXN0IG1vZGVsXwogIApgYGB7cn0KRFQ6OmRhdGF0YWJsZShyZXNfdHJhaW5fY29uY2VudHJhdGlvbnMkZnVsbC5yZXN1bHRzJHJmKQpgYGAKCgojIyBQcmVkaWN0IG5ldyBzYW1wbGVzCgo+IHByZWRpY3Rfc2FtcGxlcyh0cmFpbi5yZXN1bHQsIG5ldy5zYW1wbGVzKQoKICAqIF90cmFpbi5yZXN1bHRfOiB2YXJpYWJsZSBjb250YWluaW5nIGEgZmluYWwgbW9kZWwgb2J0YWluZWQgZnJvbSB0aGUgZnVuY3Rpb24gW3RyYWluX21vZGVsc19wZXJmb3JtYW5jZV0oInRyYWluX21vZGVsc19wZXJmb3JtYW5jZSIpOwogIAogICogX25ldy5zYW1wbGVzXzogdGhlIGRhdGEgbWF0cml4IGZyb20gYSBzcGVjbWluZSBkYXRhc2V0LCB3aGVyZSBpdCB3aWxsIGJlIHByZWRpY3RlZCwgZm9yIGVhY2ggc2FtcGxlLCB0aGUgdmFsdWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIHRyYWluZWQgdG8gcHJlZGljdCBpbiB0aGUgbW9kZWwgZ2l2ZW4gaW4gX3RyYWluLnJlc3VsdF8gYXJndW1lbnQuCgojIyMgRXhhbXBsZQoKRXhhbXBsZSBtYWtlcyB1c2Ugb2YgYW4gY29uY2VudHJhdGlvbnMgZGF0YXNldCAoW2NvbmNlbnRyYXRpb25zIGRhdGFzZXQgdXNlZF0oI3JlYWRfY29uY2VudHJhdGlvbnNfZXgpKSBhbmQgdGhlIHJlc3VsdHMgb2J0YWluZWQgaW4gdGhlIGV4YW1wbGUgZm9yIHRoZSBmdW5jdGlvbiBbdHJhaW5fbW9kZWxzX3BlcmZvcm1hbmNlXSgidHJhaW5fbW9kZWxzX3BlcmZvcm1hbmNlIikuCgpBcyB3ZSBkbyBub3QgaGF2ZSBzYW1wbGVzIHdob3NlIG91dHB1dCBpcyB1bmtvd24gZm9yIHRoZXNlIG9yIG90aGVyIGRhdGFzZXRzIGhlcmUgYmVpbmcgdXNlZCwgd2Ugd2lsbCBoZXJlIHVzZSB0aGUgc2FtZSBkYXRhIHVzZWQgdG8gdHJhaW4gdGhlIG1vZGVscyAob25seSBmb3IgZXhwbGFpbmluZyBwdXJwb3NlcykuCgpfXzEuX18gSGVyZSwgd2UgdXNlIHRoZSBQTFMgbW9kZWwgdG8gcHJlZGljdCB0aGUgb3VwdXQgb2YgdGhlIHNhbXBsZXMgZ2l2ZW46CgpgYGB7cn0KcmVzX3ByZWRpY3RfY29uY2VudHJhdGlvbnM9cHJlZGljdF9zYW1wbGVzKHJlc190cmFpbl9jb25jZW50cmF0aW9ucyRmaW5hbC5tb2RlbHMkcGxzLCBjb25jZW50cmF0aW9uc19kYXRhc2V0JGRhdGEpCmBgYAoKX18yLl9fIFRhYmxlIHdpdGggdGhlIHJlc3VsdHMuIFRoZSBmaXJzdCBjb2x1bW4gY29udGFpbnMgdGhlIG5hbWUgb2YgZWFjaCBzYW1wbGUgcHJlZGljdGVkIGFuZCB0aGUgc2Vjb25kIGNvbHVtbiB0aGUgb3V0cHV0IHZhbHVlIGdpdmVuOgoKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX3ByZWRpY3RfY29uY2VudHJhdGlvbnMpCmBgYAoKCiMjIFRyYWluIGFuZCBQcmVkaWN0CgpfX3lvdSBjYW4gdHJhaW4gbW9kZWxzIGFuZCBwcmVkaWN0IG5ldyBzYW1wbGVzIGluIG9ubHkgb25lIGZ1bmN0aW9uX18KCj4gdHJhaW5fYW5kX3ByZWRpY3QoZGF0YXNldCwgbmV3LnNhbXBsZXMsIGNvbHVtbi5jbGFzcywgbW9kZWwsIHZhbGlkYXRpb24sIG51bS5mb2xkcyA9IDEwLCBudW0ucmVwZWF0cyA9IDEwLCB0dW5lbGVuZ3RoID0gMTAsIHR1bmVncmlkID0gTlVMTCwgbWV0cmljID0gTlVMTCkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9uZXcuc2FtcGxlc186IGEgc3BlY21pbmUgZGF0YXNldCwgd2hlcmUgaXQgd2lsbCBiZSBwcmVkaWN0ZWQsIGZvciBlYWNoIHNhbXBsZSwgdGhlIHZhbHVlIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSB0cmFpbmVkIHRvIHByZWRpY3QgaW4gdGhlIG1vZGVsIGdpdmVuIGluIF9tb2RlbF8gYXJndW1lbnQ7CiAgCiAgKiBfY29sdW1uLmNsYXNzXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgbWV0YWRhdGEgdmFyaWFibGUgdG8gcHJlZGljdDsKICAKICAqIF9tb2RlbF86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1vZGVsIHRvIHRyYWluLiBUaGUgbW9kZWxzIHRoYXQgY2FuIGJlIHVzZWQgYXJlIHRoZSBvbmVzIGF2YWlsYWJsZSBmb3IgdGhlIF9jYXJldF8gcGFja2FnZS4gQSBsaXN0IHdpdGggdGhlIGF2YWlsYWJsZSBtb2RlbHMgY2FuIGJlIGFjY2Vzc2VkIFtoZXJlXShodHRwczovL3RvcGVwby5naXRodWIuaW8vY2FyZXQvYXZhaWxhYmxlLW1vZGVscy5odG1sKS4gVGhlIG5hbWVzIHRvIHVzZSBpbiB0aGlzIGFyZ3VtZW50IHRoYXQgcmVwcmVzZW50IHRoZSBtb2RlbCB3YW50ZWQgYXJlIGluIHRoZSBjb2x1bW4gXyJtZXRob2QgVmFsdWUiXy4gT25seSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgY2FuIGJlIHVzZWQgKF8iVHlwZSJfIGNvbHVtbik7CiAgCiAgKiBfdmFsaWRhdGlvbl86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHZhbGlkYXRpb24gbWV0aG9kLiBJdCBjYW4gZWl0aGVyIGJlOgogIAogICAgICArICJib290IjogUmVzYW1wbGluZzsKICAgICAgCiAgICAgICsgImN2IjogQ3Jvc3MtVmFsaWRhdGlvbjsKICAgICAgCiAgICAgICsgInJlcGVhdGVkY3YiOiBSZXBlYXRlZCBDcm9zcy12YWxpZGF0aW9uOwogICAgICAKICAgICAgKyAibG9vY3YiOiBMZWF2ZSBPbmUgT3V0IENyb3NzLVZhbGlkYXRpb247CiAgICAgIAogICAgICArICJsZ29jdiI6IExlYXZlIEdyb3VwIE91dCBDcm9zcy1WYWxpZGF0aW9uLgogICAgICAKICAqIF9udW0uZm9sZHNfOiBpZiBhIGNyb3NzIHZhbGlkYXRpb24gbWV0aG9kIGlzIGNob3NlbiwgYSBudW1lcmljIHZhbHVlIHdpdGggdGhlIG51bWJlciBvZiBmb2xkcyBtdXN0IGJlIGdpdmVuLiBEZWZhdWx0cyB0byAxMDsKICAKICAqIF9udW0ucmVwZWF0c186IGlmICJib290IiBvciAicmVwZWF0ZWRjdiIgYXJlIGNob3NlbiwgYSBudW1lcmljIHZhbHVlIHdpdGggdGhlIG51bWJlciBvZiByZXBlYXRzIG11c3QgYmUgZ2l2ZW4uIERlZmF1bHRzIHRvIDEwOwogIAogICogX3R1bmVsZW5ndGhfOiBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgdGhlIG51bWJlciBvZiBkaWZmZXJlbnQgdmFsdWVzIGZvciBlYWNoIHBhcmFtZXRlciBvZiB0aGUgbW9kZWwocykgY2hvc2VuIHNob3VsZCBiZSB0ZXN0ZWQsIHRvIGZpbmQgdGhlIGJlc3Qgb25lIGFtb25nIHRoZW0uIERlZmF1bHRzIHRvIDEwOwogIAogICogX3R1bmVncmlkXzogbGlzdCBvZiBkYXRhZnJhbWUocykgd2l0aCBwb3NzaWJsZSB0dW5pbmcgdmFsdWVzIGZvciBlYWNoIG1vZGVsIHRvIHRyYWluLiBTZWUgZXhhbXBsZXMgYmVsb3cgZm9yIG1vcmUgZGV0YWlscyAoX3N0ZXAgOF8pOwogIAogICogX21ldHJpY186IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBtZXRyaWMgdG8gdXNlIHdoZW4gZXZhbHVhdGluZyB0aGUgbW9kZWwncyBwZXJmb3JtYW5jZS4gSXQgY2FuIGVpdGhlciBiZSAiQWNjdXJhY3kiIG9yICJST0MiLgoKCiMgRmVhdHVyZSBTZWxlY3Rpb24KCiMjIEZ1bmN0aW9uIHRvIHVzZQoKPiBmZWF0dXJlX3NlbGVjdGlvbihkYXRhc2V0LCBjb2x1bW4uY2xhc3MsIG1ldGhvZCA9ICJyZmUiLCBmdW5jdGlvbnMsIHZhbGlkYXRpb24gPSAiY3YiLCByZXBlYXRzID0gNSwgbnVtYmVyID0gMTAsIHN1YnNldHMgPSAyXigyOjQpKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX2NvbHVtbi5jbGFzc186IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIHRvIHByZWRpY3Q7CiAgCiAgKiBfbWV0aG9kXzogc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgZmVhdHVyZSBzZWxlY3Rpb24gbWV0aG9kIHRvIHVzZS4gSXQgY2FuIGVpdGhlciBiZSBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoInJmZSIpIG9yIFNlbGVjdGlvbiBieSBmaWx0ZXIgKCJmaWx0ZXIiKS4gRGVmYXVsdHMgdG8gInJmZSI7CiAgCiAgKiBfZnVuY3Rpb25zXzogZnVuY3Rpb24gdG8gdXNlIGluIG1vZGVsIGZpdHRpbmcgcHJlZGljdGlvbiBhbmQgdmFyaWFibGUgaW1wb3J0YW5jZS9maWx0ZXJpbmcuIEl0IGNhbiBlaXRoZXJiZToKICAKICAgICAgKyBSYW5kb20gRm9yZXN0czogYGZ1bmN0aW9ucz1jYXJldDo6cmZGdW5jc2AsIGlmIGBtZXRob2Q9InJmZSJgLCBvciBgZnVuY3Rpb25zPWNhcmV0OjpyZlNCRmAsIGlmIGBtZXRob2Q9ImZpbHRlciJgOwogICAgICAKICAgICAgKyBMaW5lYXIgUmVncmVzc2lvbjogYGZ1bmN0aW9ucz1jYXJldDo6bG1GdW5jc2AsIGlmIGBtZXRob2Q9InJmZSJgLCBvciBgZnVuY3Rpb25zPWNhcmV0OjpsbVNCRmAsIGlmIGBtZXRob2Q9ImZpbHRlciJgOwogICAgICAKICAgICAgKyBCYWdnZWQgVHJlZXM6IGBmdW5jdGlvbnM9Y2FyZXQ6OnRyZWViYWdGdW5jc2AsIGlmIGBtZXRob2Q9InJmZSJgLCBvciBgZnVuY3Rpb25zPWNhcmV0Ojp0cmVlYmFnU0JGYCwgaWYgYG1ldGhvZD0iZmlsdGVyImA7CiAgICAgIAogICAgICArIExpbmVhciBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKExEQSk6IGBmdW5jdGlvbnM9Y2FyZXQ6OmxkYUZ1bmNzYCwgaWYgYG1ldGhvZD0icmZlImAsIG9yIGBmdW5jdGlvbnM9Y2FyZXQ6OmxkYVNCRmAsIGlmIGBtZXRob2Q9ImZpbHRlciJgOwogICAgICAKICAgICAgKyBOYWl2ZS1CYXllczogYGZ1bmN0aW9ucz1jYXJldDo6bmJGdW5jc2AsIGlmIGBtZXRob2Q9InJmZSJgLCBvciBgZnVuY3Rpb25zPWNhcmV0OjpuYlNCRmAsIGlmIGBtZXRob2Q9ImZpbHRlciJgLgogIAogICogX3ZhbGlkYXRpb25fOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSB2YWxpZGF0aW9uIG1ldGhvZC4gSXQgY2FuIGVpdGhlciBiZToKICAKICAgICAgKyAiYm9vdCI6IFJlc2FtcGxpbmc7CiAgICAgIAogICAgICArICJjdiI6IENyb3NzLVZhbGlkYXRpb247CiAgICAgIAogICAgICArICJyZXBlYXRlZGN2IjogUmVwZWF0ZWQgQ3Jvc3MtdmFsaWRhdGlvbjsKICAgICAgCiAgICAgICsgImxvb2N2IjogTGVhdmUgT25lIE91dCBDcm9zcy1WYWxpZGF0aW9uOwogICAgICAKICAgICAgKyAibGdvY3YiOiBMZWF2ZSBHcm91cCBPdXQgQ3Jvc3MtVmFsaWRhdGlvbi4KICAgICAgCiAgKiBfcmVwZWF0c186IGlmICJib290IiBvciAicmVwZWF0ZWRjdiIgYXJlIGNob3NlbiwgYSBudW1lcmljIHZhbHVlIHdpdGggdGhlIG51bWJlciBvZiByZXBlYXRzIG11c3QgYmUgZ2l2ZW4uIERlZmF1bHRzIHRvIDU7CiAgCiAgKiBfbnVtYmVyXzogaWYgYSBjcm9zcyB2YWxpZGF0aW9uIG1ldGhvZCBpcyBjaG9zZW4sIGEgbnVtZXJpYyB2YWx1ZSB3aXRoIHRoZSBudW1iZXIgb2YgZm9sZHMgbXVzdCBiZSBnaXZlbi4gRGVmYXVsdHMgdG8gMTA7CiAgCiAgKiBfc3Vic2V0c186IG51bWVyaWMgdmVjdG9yIGluZGljYXRpbmcgdGhlIG51bWJlciBvZiBmZWF0dXJlcyBmb3IgZWFjaCBncm91cCBvZiB0ZXN0LiBPbmx5IG5lY2Vzc2FyeSB0byBpbmRpY2F0ZSB3aGVuIG1ldGhvZD0icmZlIi4gRGVmYXVsdHMgdG8gMl4oMjo0KS4KCiMjIEV4YW1wbGUKCkV4YW1wbGUgbWFrZXMgdXNlIG9mIGFuIGNvbmNlbnRyYXRpb25zIGRhdGFzZXQgKFtjb25jZW50cmF0aW9ucyBkYXRhc2V0IHVzZWRdKCNyZWFkX2NvbmNlbnRyYXRpb25zX2V4KSkuCgpfXzEuX18gUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24sIHdoZXJlIHRoZSBmdW5jdGlvbiBjaG9zZW4gdG8gZG8gbW9kZWwgZml0dGluZyBwcmVkaWN0aW9uIGFuZCB2YXJpYWJsZSBpbXBvcnRhbmNlL2ZpbHRlcmluZyBpcyBgcmZGdW5jc2AgKHJhbmRvbSBmb3Jlc3RzKS4gRm9yIG1vZGVsIHZhbGlkYXRpb24sIHRoZSBjcm9zcyB2YWxpZGF0aW9uIG1ldGhvZCB3aXRoIDMgZm9sZHMgd2FzIHVzZWQ6CgpgYGB7cn0KcmVzX2ZzX3JmZV9jb25jZW50cmF0aW9ucz1mZWF0dXJlX3NlbGVjdGlvbihjb25jZW50cmF0aW9uc19kYXRhc2V0LCAiTXVzY2xlLmxvc3MiLCBtZXRob2Q9InJmZSIsIGZ1bmN0aW9ucyA9IGNhcmV0OjpyZkZ1bmNzLCB2YWxpZGF0aW9uID0gImN2IiwgbnVtYmVyID0gMywgc3Vic2V0cyA9IDJeKDE6NikpCmBgYAoKX18yLl9fIFN1bW1hcnkgb2YgdGhlIHJlc3VsdHM6CgpgYGB7cn0KcmVzX2ZzX3JmZV9jb25jZW50cmF0aW9ucwpgYGAKCl9fMy5fXyBOYW1lIG9mIHRoZSB2YXJpYWJsZXMgdGhhdCBtYWtlIHRoZSBiZXN0IGdyb3VwOgoKYGBge3J9CnJlc19mc19yZmVfY29uY2VudHJhdGlvbnMkb3B0VmFyaWFibGVzCmBgYAoKX180Ll9fIFBlcmZvcm1hbmNlIHBsb3Q6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGxvdChyZXNfZnNfcmZlX2NvbmNlbnRyYXRpb25zLCB0eXBlPWMoImciLCJvIiksIG1haW49IlBlcmZvcm1hbmNlIFByb2ZpbGUgYWNyb3NzIHRoZSBkaWZmZXJlbnQgc3Vic2V0IHNpemVzIikKYGBgCgoKCgoKIyBSZWdyZXNzaW9uIEFuYWx5c2lzCgojIyBGdW5jdGlvbnMgdG8gdXNlCgojIyMgVG8gcnVuIGFuYWx5c2lzCgpfX1lvdSBjYW4gcGVyZm9ybSBsaW5lYXIgcmVncmVzc2lvbiBpbiBvbmUgc3BlY2lmaWMgZGF0YSB2YXJpYWJsZV9fCgo+IGxpbnJlZ3Jlc3Npb25fb25ldmFyKGRhdGFzZXQsIHgudmFsLCBtZXRhZGF0YS52YXJzLCBjb21iaW5hdGlvbikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF94LnZhbF86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIHZhcmlhYmxlIHRvIGJlIHRlc3RlZCBpbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXM7CiAgCiAgKiBfbWV0YWRhdGEudmFyc186IHN0cmluZyBvciBjaGFyYWN0ZXIgdmVjdG9yIGluZGljYXRpbmcgdGhlIG1ldGFkYXRhIHZhcmlhYmxlKHMpIHRvIHVzZSBpbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb247CiAgCiAgKiBfY29tYmluYXRpb25fOiBzdHJpbmcgcmVwcmVzZW50aW5nIGEgZm9ybXVsYSBzcGVjaWZ5aW5nIHRoZSBtb2RlbC4gRm9yIGV4YW1wbGUsIGlmIF9tZXRhZGF0YS52YXJzXyBpcyBjKCJ2YXIxIiwgInZhcjIiKSwgY29tYmluYXRpb24gY291bGQgYmUgInZhcjErdmFyMiIuCgpfX1lvdSBjYW4gYWxzbyBwZXJmb3JtIGxpbmVhciByZWdyZXNzaW9uIGluIGVhY2ggZGF0YSB2YXJpYWJsZSBhdCBvbmNlX18KCj4gbGlucmVnX2FsbF92YXJzKGRhdGFzZXQsIG1ldGFkYXRhLnZhcnMsIGNvbWJpbmF0aW9uKSA8YSBuYW1lPSJsaW5yZWdfYWxsX3ZhcnMiPjwvYT4KCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9tZXRhZGF0YS52YXJzXzogc3RyaW5nIG9yIGNoYXJhY3RlciB2ZWN0b3IgaW5kaWNhdGluZyB0aGUgbWV0YWRhdGEgdmFyaWFibGUocykgdG8gdXNlIGluIHRoZSBsaW5lYXIgcmVncmVzc2lvbjsKICAKICAqIF9jb21iaW5hdGlvbl86IHN0cmluZyByZXByZXNlbnRpbmcgYSBmb3JtdWxhIHNwZWNpZnlpbmcgdGhlIG1vZGVsLiBGb3IgZXhhbXBsZSwgaWYgX21ldGFkYXRhLnZhcnNfIGlzIGMoInZhcjEiLCAidmFyMiIpLCBjb21iaW5hdGlvbiBjb3VsZCBiZSAidmFyMSt2YXIyIi4KCiMjIyBUbyB2aXN1YWxpemUgcmVzdWx0cwoKX1RoZXNlIGZ1bmN0aW9ucyBvbmx5IGNhbiBiZSB1c2VkIGZvciByZXN1bHRzIGZyb20gdGhlIGZ1bmN0aW9uIFtsaW5yZWdfYWxsX3ZhcnNdKCNsaW5yZWdfYWxsX3ZhcnMpLl8KCl9fWW91IGNhbiBzZWUgYSBkYXRhZnJhbWUgd2l0aCB0aGUgY29lZmZpY2llbnQgdmFsdWVzX18KCj4gbGlucmVnX2NvZWZfdGFibGUobGlucmVnLnJlc3VsdHMsIHdyaXRlLmZpbGUgPSBGLCBmaWxlLm91dCA9ICJsaW5yZWctY29lZnMuY3N2IikKCiAgKiBfbGlucmVnLnJlc3VsdHNfOiB2YXJpYWJsZSBjb250YWluaW5nIHRoZSBsaW5lYXIgcmVncmVzc2lvbiByZXN1bHRzLCBvYnRhaW5lZCBmcm9tIFtsaW5yZWdfYWxsX3ZhcnNdKCNsaW5yZWdfYWxsX3ZhcnMpIGZ1bmN0aW9uOwogIAogICogX3dyaXRlLmZpbGVfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIHlvdSB3YW50IHRoZSByZXN1bHRzIHRvIGJlIHdyaXR0ZW4gaW4gYSBmaWxlLiBEZWZhdWx0cyB0byBGQUxTRTsKICAKICAqIF9maWxlLm91dF86IHN0cmluZyB3aXRoIHRoZSBmaWxlIHBhdGggdG8gd3JpdGUgdG8uIFlvdSBvbmx5IG5lZWQgdG8gZ2l2ZSB0aGlzIGluZm9ybWF0aW9uIGlmIHlvdSBoYXZlIGNob3NlbiB0byB3cml0ZSB0aGUgcmVzdWx0cyB0byBhIGZpbGUgKHdyaXRlLmZpbGU9VCkuIERlZmF1bHRzIHRvICJsaW5yZWctY29lZnMuY3N2Ii4KICAKX19Zb3UgY2FuIHNlZSBhIGRhdGFmcmFtZSB3aXRoIHRoZSBwLXZhbHVlc19fCiAgCj4gbGlucmVnX3B2YWx1ZV90YWJsZShsaW5yZWcucmVzdWx0cywgd3JpdGUuZmlsZSA9IEYsIGZpbGUub3V0ID0gImxpbnJlZy1wdmFsdWVzLmNzdiIpCgogICogX2xpbnJlZy5yZXN1bHRzXzogdmFyaWFibGUgY29udGFpbmluZyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gcmVzdWx0cywgb2J0YWluZWQgZnJvbSBbbGlucmVnX2FsbF92YXJzXSgjbGlucmVnX2FsbF92YXJzKSBmdW5jdGlvbjsKICAKICAqIF93cml0ZS5maWxlXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiB5b3Ugd2FudCB0aGUgcmVzdWx0cyB0byBiZSB3cml0dGVuIGluIGEgZmlsZS4gRGVmYXVsdHMgdG8gRkFMU0U7CiAgCiAgKiBfZmlsZS5vdXRfOiBzdHJpbmcgd2l0aCB0aGUgZmlsZSBwYXRoIHRvIHdyaXRlIHRvLiBZb3Ugb25seSBuZWVkIHRvIGdpdmUgdGhpcyBpbmZvcm1hdGlvbiBpZiB5b3UgaGF2ZSBjaG9zZW4gdG8gd3JpdGUgdGhlIHJlc3VsdHMgdG8gYSBmaWxlICh3cml0ZS5maWxlPVQpLiBEZWZhdWx0cyB0byAibGlucmVnLXB2YWx1ZXMuY3N2Ii4KCl9fWW91IGNhbiBzZWUgYSBkYXRhZnJhbWUgd2l0aCB0aGUgci1zcXVhcmVkIHZhbHVlc19fCgo+IGxpbnJlZ19yc3F1YXJlZChsaW5yZWcucmVzdWx0cywgd3JpdGUuZmlsZSA9IEYsIGZpbGUub3V0ID0gImxpbnJlZy1yc3F1YXJlZC5jc3YiKQoKICAqIF9saW5yZWcucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGxpbmVhciByZWdyZXNzaW9uIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gW2xpbnJlZ19hbGxfdmFyc10oI2xpbnJlZ19hbGxfdmFycykgZnVuY3Rpb247CiAgCiAgKiBfd3JpdGUuZmlsZV86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFMU0UpIGluZGljYXRpbmcgaWYgeW91IHdhbnQgdGhlIHJlc3VsdHMgdG8gYmUgd3JpdHRlbiBpbiBhIGZpbGUuIERlZmF1bHRzIHRvIEZBTFNFOwogIAogICogX2ZpbGUub3V0Xzogc3RyaW5nIHdpdGggdGhlIGZpbGUgcGF0aCB0byB3cml0ZSB0by4gWW91IG9ubHkgbmVlZCB0byBnaXZlIHRoaXMgaW5mb3JtYXRpb24gaWYgeW91IGhhdmUgY2hvc2VuIHRvIHdyaXRlIHRoZSByZXN1bHRzIHRvIGEgZmlsZSAod3JpdGUuZmlsZT1UKS4gRGVmYXVsdHMgdG8gImxpbnJlZy1yc3F1YXJlZC5jc3YiLgoKX19Zb3UgY2FuIHNlZSBhIHBsb3Qgb2YgdGhlIGNvZWZmaWNpZW50IGFuZCBwLXZhbHVlc19fCgo+IHBsb3RfcmVncmVzc2lvbl9jb2Vmc19wdmFsdWVzKGxpbnJlZy5yZXN1bHRzLCBiYXIuY29sID0gTlVMTCwgY29lZi5zaXplID0gNSwgLi4uKQoKICAqIF9saW5yZWcucmVzdWx0c186IHZhcmlhYmxlIGNvbnRhaW5pbmcgdGhlIGxpbmVhciByZWdyZXNzaW9uIHJlc3VsdHMsIG9idGFpbmVkIGZyb20gW2xpbnJlZ19hbGxfdmFyc10oI2xpbnJlZ19hbGxfdmFycykgZnVuY3Rpb247CiAgCiAgKiBfYmFyLmNvbF86IGNoYXJhY3RlciB2ZWN0b3IgcmVwcmVzZW50aW5nIHRoZSBjb2xvcnMgb2YgdGhlIGJhcnMgaW4gdGhlIHBsb3QuIE9uZSBjb2xvciBmb3IgZWFjaCB2YXJpYWJsZSByZXByZXNlbnRlZDsgX19vcHRpb25hbF9fCiAgCiAgKiBfY29lZi5zaXplXzogbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIHRoZSByZWxhdGl2ZSBzaXplIG9mIHRoZSBmb250IG9mIGNvZWZmaWNpZW50IHZhbHVlczsKICAKICAqIF8uLi5fOiBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgZm9yIGZ1bmN0aW9uIFtnZW9tX2Jhcl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dncGxvdDIvdmVyc2lvbnMvMi4yLjEvdG9waWNzL2dlb21fYmFyKS4KCiMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBubXItcGVha3MgZGF0YXNldCAoW05NUiBwZWFrcyBkYXRhc2V0IHVzZWRdKCNyZWFkX25tcl9wZWFrc19leCkpLCBhbHJlYWR5IHRyZWF0ZWQgZm9yIG1pc3NpbmcgdmFsdWVzIGluIGEgcHJldmlvdXMgZXhhbXBsZS4KCl9fMS5fXyBMaW5lYXIgcmVncmVzc2lvbiBvbiBhbGwgdmFyaWFibGVzOgoKYGBge3J9CnJlc19saW5yZWdfbm1yX3BlYWtzPWxpbnJlZ19hbGxfdmFycyhubXJfcGVha3NfZGF0YXNldF9tdiwgYygiYWdyb3JlZ2lvbnMiLCAic2Vhc29ucyIpLCAiYWdyb3JlZ2lvbnMqc2Vhc29ucyIpCmBgYAoKX18yLl9fIFRhYmxlIHdpdGggdGhlIGNvZWZmaWNpZW50IHZhbHVlcyBmb3IgZWFjaCBjb21iaW5hdGlvbiAiYWdyb3JlZ2lvbnMqc2Vhc29ucyIgb2YgdHdvIHZhcmlhYmxlczoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKGxpbnJlZ19jb2VmX3RhYmxlKHJlc19saW5yZWdfbm1yX3BlYWtzKSwgb3B0aW9ucz1saXN0KHNjcm9sbFg9VCkpCmBgYAoKX18zLl9fIFRhYmxlIHdpdGggdGhlIHAtdmFsdWVzIGZvciBlYWNoIGNvbWJpbmF0aW9uICJhZ3JvcmVnaW9ucypzZWFzb25zIiBvZiB0d28gdmFyaWFibGVzOgoKYGBge3J9CkRUOjpkYXRhdGFibGUobGlucmVnX3B2YWx1ZV90YWJsZShyZXNfbGlucmVnX25tcl9wZWFrcyksIG9wdGlvbnM9bGlzdChzY3JvbGxYPVQpKQpgYGAKCl9fNC5fXyBUYWJsZSB3aXRoIHRoZSByLXNxdWFyZWQgdmFsdWVzIGZvciBlYWNoIGNvbWJpbmF0aW9uICJhZ3JvcmVnaW9ucypzZWFzb25zIiBvZiB0d28gdmFyaWFibGVzOgoKYGBge3J9CkRUOjpkYXRhdGFibGUobGlucmVnX3JzcXVhcmVkKHJlc19saW5yZWdfbm1yX3BlYWtzKSwgb3B0aW9ucz1saXN0KHNjcm9sbFg9VCkpCmBgYAoKX181Ll9fIFBsb3Qgb2YgdGhlIGNvZWZmaWNpZW50IGFuZCBwLXZhbHVlcyBvZiB0aGUgdmFyaWFibGVzIDAuOTggYW5kIDMuODEgaW4gdGhlIHJlc3VsdHM6CgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGxvdF9yZWdyZXNzaW9uX2NvZWZzX3B2YWx1ZXMocmVzX2xpbnJlZ19ubXJfcGVha3NbYygiMC45OCIsICIzLjgxIildLCBiYXIuY29sPWMoImJsdWUiLCAiY3lhbiIpKQpgYGAKCgoKIyBDb3JyZWxhdGlvbiBBbmFseXNpcwoKIyMgRnVuY3Rpb25zIHRvIHVzZQoKX19Zb3UgY2FuIHBlcmZvcm0gYSBjb3JyZWxhdGlvbnMgdGVzdCBvbiB0d28gdmFyaWFibGVzIG9yIHNhbXBsZXMgZnJvbSB0aGUgZGF0YXNldF9fCgo+IGNvcnJlbGF0aW9uX3Rlc3QoZGF0YXNldCwgeCwgeSwgbWV0aG9kID0gInBlYXJzb24iLCBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCBieS52YXIgPSBUKQoKICAqIF9kYXRhc2V0XyBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfeF86IG9uZSBvZiB0aGUgdHdvIHZhcmlhYmxlcy8gc2FtcGxlcyB0aGF0IHdpbGwgYmUgdXNlZCBpbiB0aGUgdGVzdDsKICAKICAqIF95XzogdGhlIG90aGVyIHZhcmlhYmxlLyBzYW1wbGUgdGhhdCB3aWxsIGJlIHVzZWQgaW4gdGhlIHRlc3Q7CiAgCiAgKiBfbWV0aG9kXzogQ29ycmVsYXRpb24gbWV0aG9kLiBJdCBjYW4gZWl0aGVyIGJlICJwZWFyc29uIiwgImtlbmRhbGwiIG9yICJzcGVhcm1hbiIuIERlZmF1bHRzIHRvICJwZWFyc29uIjsKICAKICAqIF9hbHRlcm5hdGl2ZV86IHN0cmluZyByZXByZXNlbnRpbmcgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgKEgxKS4gSXQgY2FuIGVpdGhlciBiZToKICAKICAgICAgKyBEaWZmZXJlbnQgZnJvbSB0aGUgbnVsbCBoeXBvdGhlc2lzIChIMDogeD1hIGFuZCBIMTogeCE9YSk6InR3by5zaWRlZCI7CiAgICAgIAogICAgICArIEdyZWF0ZXIgdGhhbiB0aGUgbnVsbCBoeXBvdGhlc2lzIChIMDogeD1hIGFuZCBIMTogeD5hKTogImdyZWF0ZXIiOwogICAgICAKICAgICAgKyBMZXNzIHRoYW4gdGhlIG51bGwgaHlwb3RoZXNpcyAoSDA6IHg9YSBhbmQgSDE6IHg8YSk6ICJsZXNzIjsKICAgICAgCiAgKiBfYnkudmFyXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQWxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBjb3JyZWxhdGlvbnMgdGVzdCBpcyBiZWluZyBwZXJmb3JtZWQgdG8gdmFyaWFibGVzIChieS52YXI9VFJVRSkgb3IgdG8gc2FtcGxlcyAoYnkudmFyPUZBTFNFKS4gRGVmYXVsdHMgdG8gVFJVRS4KICAKX19Zb3UgY2FuIHBlcmZvcm0gY29ycmVsYXRpb25zIHRlc3QgYmV0d2VlbiBhbGwgdmFyaWFibGVzIG9yIHNhbXBsZXMgZnJvbSB0aGUgZGF0YXNldF9fCgo+IGNvcnJlbGF0aW9uc190ZXN0KGRhdGFzZXQsIG1ldGhvZCA9ICJwZWFyc29uIiwgYnkudmFyID0gVCwgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIikKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF9tZXRob2RfOiBDb3JyZWxhdGlvbiBtZXRob2QuIEl0IGNhbiBlaXRoZXIgYmUgInBlYXJzb24iLCAia2VuZGFsbCIgb3IgInNwZWFybWFuIi4gRGVmYXVsdHMgdG8gInBlYXJzb24iOwogIAogICogX2J5LnZhcl86IGJvb2xlYW4gdmFsdWUgKFRSVUUgb3IgRkFsU0UpIGluZGljYXRpbmcgd2hldGhlciB0aGUgY29ycmVsYXRpb25zIHRlc3QgaXMgYmVpbmcgcGVyZm9ybWVkIHRvIHZhcmlhYmxlcyAoYnkudmFyPVRSVUUpIG9yIHRvIHNhbXBsZXMgKGJ5LnZhcj1GQUxTRSkuIERlZmF1bHRzIHRvIFRSVUU7CiAgCiAgKiBfYWx0ZXJuYXRpdmVfOiBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIChIMSkuIEl0IGNhbiBlaXRoZXIgYmU6CiAgCiAgICAgICsgRGlmZmVyZW50IGZyb20gdGhlIG51bGwgaHlwb3RoZXNpcyAoSDA6IHg9YSBhbmQgSDE6IHghPWEpOiJ0d28uc2lkZWQiOwogICAgICAKICAgICAgKyBHcmVhdGVyIHRoYW4gdGhlIG51bGwgaHlwb3RoZXNpcyAoSDA6IHg9YSBhbmQgSDE6IHg+YSk6ICJncmVhdGVyIjsKICAgICAgCiAgICAgICsgTGVzcyB0aGFuIHRoZSBudWxsIGh5cG90aGVzaXMgKEgwOiB4PWEgYW5kIEgxOiB4PGEpOiAibGVzcyIuCgpfX1lvdSBjYW4gYWxzbyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGFsbCB2YXJpYWJsZXMgb3Igc2FtcGxlcyBmcm9tIHRoZSBkYXRhc2V0X18KCj4gY29ycmVsYXRpb25zX2RhdGFzZXQoZGF0YXNldCwgbWV0aG9kID0gInBlYXJzb24iLCBieS52YXIgPSBUKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX21ldGhvZF86IENvcnJlbGF0aW9uIG1ldGhvZC4gSXQgY2FuIGVpdGhlciBiZSAicGVhcnNvbiIsICJrZW5kYWxsIiBvciAic3BlYXJtYW4iLiBEZWZhdWx0cyB0byAicGVhcnNvbiI7CiAgCiAgKiBfYnkudmFyXzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQWxTRSkgaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBjb3JyZWxhdGlvbnMgdGVzdCBpcyBiZWluZyBwZXJmb3JtZWQgdG8gdmFyaWFibGVzIChieS52YXI9VFJVRSkgb3IgdG8gc2FtcGxlcyAoYnkudmFyPUZBTFNFKS4gRGVmYXVsdHMgdG8gVFJVRS4KCl9fWW91IGNhbiBwbG90IGEgaGVhdG1hcCB3aXRoIHRoZSBjb3JyZWxhdGlvbiB2YWx1ZXMgb2J0YWluZWQgZnJvbSB0aGUgcHJldmlvdXMgZnVuY3Rpb25fXwoKPiBoZWF0bWFwX2NvcnJlbGF0aW9ucyhjb3JyZWxhdGlvbnMsIGNvbCA9IE5VTEwsIC4uLikKCiAgKiBfY29ycmVsYXRpb25zXzogY29ycmVsYXRpb25zIG1hdHJpeCBvYnRhaW5lZCBmcm9tIGZ1bmN0aW9uIF9jb3JyZWxhdGlvbnNcX2RhdGFzZXRfLCBleHBsYWluZWQgYWJvdmU7CiAgCiAgKiBfY29sXzogY2hhcmFjdGVyIHZlY3RvciB3aXRoIHRoZSBjb2xvcnMgdG8gYmUgdXNlZCBvbiB0aGUgaGVhdG1hcC4gSWYgbm90IGdpdmVuLCBwcmVkZWZpbmVkIGNvbG9yIHBhbGxldHRlIHdpbGwgYmUgdXNlZDsgX19vcHRpb25hbF9fCiAgCiAgKiBfLi4uXzogYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gcGVyc29uYWxpemUgdGhlIGhlYXRtYXAuIFNlZSBmdW5jdGlvbiBbaGVhdG1hcF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9oZWF0bWFwKSBmb3IgZGV0YWlscy4KCiMjIEV4YW1wbGVzCgpFeGFtcGxlIG1ha2VzIHVzZSBvZiBhbiBjb25jZW50cmF0aW9ucyBkYXRhc2V0IChbY29uY2VudHJhdGlvbnMgZGF0YXNldCB1c2VkXSgjcmVhZF9jb25jZW50cmF0aW9uc19leCkpLgoKX18xLl9fIFBlcmZvcm0gY29ycmVsYXRpb25zIHRlc3RzIG9uIGFsbCBzYW1wbGVzIGZyb20gdGhlIGRhdGFzZXQsIHVzaW5nIFBlYXJzb24gbWV0aG9kOgoKYGBge3J9CnJlc19jb3JfdGVzdF9jb25jZW50cmF0aW9ucyA9IGNvcnJlbGF0aW9uc190ZXN0KGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIGJ5LnZhciA9IEZBTFNFKQpgYGAKCl9fMi5fXyBUYWJsZSB3aXRoIHRoZSByZXN1bHRzOgoKYGBge3J9CkRUOjpkYXRhdGFibGUocmVzX2Nvcl90ZXN0X2NvbmNlbnRyYXRpb25zLCBvcHRpb25zPWxpc3Qoc2Nyb2xsWD1UKSkKYGBgCgpfXzMuX18gQ2FsY3VsYXRlIGNvcnJlbGF0aW9ucyB2YWx1ZXMgYmV0d2VlbiBhbGwgc2FtcGxlcyBmcm9tIHRoZSBkYXRhc2V0OgoKYGBge3J9CnJlc19jb3JfdmFsX2NvbmNlbnRyYXRpb25zPWNvcnJlbGF0aW9uc19kYXRhc2V0KGNvbmNlbnRyYXRpb25zX2RhdGFzZXQsIGJ5LnZhcj1GKQpgYGAKCl9fNC5fXyBIZWF0bWFwIG9mIHRoZSBjb3JyZWxhdGlvbnMgY2FsY3VsYXRlZCBpbiBzdGVwIF8zLl86CgpfSGVyZSBpcyBzaG93biBhbiBleGFtcGxlIG1ha2luZyB1c2Ugb2YgYSBjb2xvciBwYWxsZXR0ZSwgZ2l2ZW4gdGhyb3VnaCB0aGUgYXJndW1lbnQgX2NvbF8gXwoKX1NlZSBfcGFsbGV0dGVfIGFyZ3VtZW50IGZyb20gW3BjYV9zY29yZXNwbG90MkRdKCNwY2Ffc2NvcmVzcGxvdDJEKSBmdW5jdGlvbiBmb3IgZGV0YWlscy5fCgpgYGB7ciBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KY29sb3JzPXJldihjb2xvclJhbXBQYWxldHRlKFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMCwgIlJkQnUiKSkoMjU2KSkgI09idGFpbiBhIGNoYXJhY3RlciB2ZWN0b3Igd2l0aCB0aGUgY29sb3IgZnJvbSB0aGUgJ1JkQnUnIGNvbG9yIHBhbGxldHRlLCBpbiByZXZlcnNlZCBtYW5uZXIuIEl0IGlzIHRoaXMgY2hhcmFjdGVyIHZlY3RvciB0aGF0IHdpbGwgYmUgZ2l2ZW4gdG8gdGhlIGNvbCBhcmd1bWVudAoKaGVhdG1hcF9jb3JyZWxhdGlvbnMocmVzX2Nvcl92YWxfY29uY2VudHJhdGlvbnMsIGNvbD1jb2xvcnMpCmBgYAoKCgojIE1ldGFib2xpdGUgSWRlbnRpZmljYXRpb24KCklkZW50aWZpY2F0aW9uIG9mIG1ldGFib2xpdGVzIGlzIG9ubHkgYXZhaWxhYmxlIGZvciBMQy1NUyBzcGVjdHJhIGFuZCBOTVIgUGVha3MuCgojIyBMQy1NUyBTcGVjdHJhCgojIyMgRnVuY3Rpb24gdG8gdXNlCgo+IE1BSVRfaWRlbnRpZnlfbWV0YWJvbGl0ZXMoZGF0YXNldCwgbWV0YWRhdGEudmFyaWFibGUsIHhTZXQgPSBOVUxMLCBkYXRhLmZvbGRlciA9IE5VTEwsIGZlYXR1cmVzID0gTlVMTCwgbWFzcy50b2xlcmFuY2UgPSAwLjUpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbWV0YWRhdGEudmFyaWFibGVfOiBuYW1lIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSB0aGF0IGNhbiBoZWxwIGluIHRoZSBpZGVudGlmaWNhdGlvbiBvZiB0aGUgbWV0YWJvbGl0ZXM7CiAgCiAgKiBfeFNldF86IHhjbXNTZXQgb2JqZWN0IHRoYXQgY2FuIGJlIHBhc3NlZC4gRGVmYXVsdHMgdG8gTlVMTC4gTm9ybWFsbHksIGl0IGlzIHhTZXQ9ZGF0YXNldCR4U2V0OwogIAogICogX2RhdGEuZm9sZGVyXzogc3RyaW5nIGNvbnRhaW5pbmcgdGhlIHBhdGggb2YgdGhlIGRhdGEgZm9sZGVyLiBTaG91bGQgY29udGFpbiB0aGUgZGF0YSByZWFkIGludG8gdGhlIHNwZWNtaW5lIGRhdGFzZXQgZ2l2ZW47CiAgCiAgKiBfZmVhdHVyZXNfOiBmZWF0dXJlcyB0aGF0IGNhbiBiZSB1c2VkIHRvIGhlbHAgdG8gaWRlbnRpZnkgdGhlIG1ldGFib2xpdGVzOwogIAogICogX21hc3MudG9sZXJhbmNlXzogbWFzcyB0b2xlcmFuY2Ugd2hlbiBtYXRjaGluZyByZWZlcmVuY2UgbWV0YWJvbGl0ZXMgdG8gdGhlIGRhdGFzZXQuCgoKIyMjIEV4YW1wbGUKCkV4YW1wbGUgbWFrZXMgdXNlIG9mIHRoaXMgW0xDLU1TIGRhdGFzZXRdKCNyZWFkX21zX3NwZWN0cmFfZXgpLgoKX18xLl9fIElkZW50aWZpY2F0aW9uIG9mIG1ldGFib2xpdGVzLiBUaGUgbWV0YWRhdGEgdmFyaWFibGUgdGhhdCB3aWxsIGhlbHAgb24gdGhlIGlkZW50aWZpY2F0aW9uIGlzICJ0eXBlIiAoaW4gdGhpcyBkYXRhc2V0LCBpdCBpcyB0aGUgb25seSBtZXRhZGF0YSB2YXJpYWJsZSBhdmFpbGFibGUpIGFuZCBhbGwgZmVhdHVyZXMgKHZhcmlhYmxlcywgaS5lLiwgbWFzcy9jaGFyZ2UgcmF0aW9zKSB3aWxsIGJlIHVzZWQgdG8gaWRlbnRpZnkgdGhlIG1ldGFib2xpdGVzOgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CmxpYnJhcnkoTUFJVCkKYGBgCgpgYGB7cn0KcmVzX2lkX2xjbXM9TUFJVF9pZGVudGlmeV9tZXRhYm9saXRlcyhsY21zX2RhdGFzZXQsICJ0eXBlIiwgZmVhdHVyZXMgPSAiYWxsIiwgZGF0YS5mb2xkZXI9bGNtc19kYXRhX2ZvbGRlciwgeFNldD1sY21zX2RhdGFzZXQkeFNldCkKYGBgCgpfXzIuX18gVGFibGUgd2l0aCB0aGUgcmVzdWx0czoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlc19pZF9sY21zQEZlYXR1cmVJbmZvQG1ldGFib2xpdGVUYWJsZSwgb3B0aW9ucz1saXN0KHNjcm9sbFg9VCkpCmBgYAoKX18zLl9fIFRhYmxlIHdpdGggZmlsdGVyZWQgcmVzdWx0cy4gT25seSBrbm93biBtZXRhYm9saXRlcyBhcmUgaGVyZSBzaG93biwgYXMgd2VsbCBvbmx5IHNwZWNpZmljIGNvbHVtbnMgd2VyZSBzZWxlY3RlZDoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHJlc19pZF9sY21zQEZlYXR1cmVJbmZvQG1ldGFib2xpdGVUYWJsZVt3aGljaChyZXNfaWRfbGNtc0BGZWF0dXJlSW5mb0BtZXRhYm9saXRlVGFibGUkTmFtZSE9J1Vua25vd24nKSxjKDYsOSwxLDIsMyw0LDUsNyw4LDEwLDExKV0sIG9wdGlvbnM9bGlzdChzY3JvbGxYPVQpKQpgYGAKCgojIyBOTVIgUGVha3MKCiMjIyBGdW5jdGlvbiB0byB1c2UKCj4gbm1yX2lkZW50aWZpY2F0aW9uKGRhdGFzZXQsIHBwbS50b2w9MC4wMywgY2x1c3QubWV0aG9kPSdwZWFyc29uJywgY2x1c3QudHJlc2hvbGQ9TlVMTCwgY2x1c3QucGVha3MubWluPTIsIGNsdXN0Lm1heFBlYWtzPTQwLCBjbHVzdC5uVG9wPTUsIGZyZXE9NTAwLCBudWNsPSIxSCIsIHNvbHY9TlVMTCwgcEg9TlVMTCwgdGVtcD1OVUxMKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3BwbS50b2xfOiBwcG0gdG9sZXJhbmNlIHdoZW4gbWF0Y2hpbmcgcmVmZXJlbmNlIHBlYWtzIHRvIHRoZSBkYXRhc2V0IHBlYWtzOwogIAogICogX2NsdXN0Lm1ldGhvZF86IGNvcnJlbGF0aW9uIG1ldGhvZCB0byB1c2UgaW4gdGhlIGZvcm1hdGlvbiBvZiBjbHVzdGVycy4gRGVmYXVsdHMgdG8gInBlYXJzb24iOwogIAogICogX2NsdXN0LnRyZXNob2xkXzogbWluaW11bSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyB0byBmb3JtIGNsdXN0ZXJzLiBJZiBub3QgZ2l2ZW4gKGNsdXN0LnRyZXNob2xkPU5VTEwpLCB0aGUgZnVuY3Rpb24gY2FsY3VsYXRlcyB0aGUgb3B0aW11bSB2YWx1ZSAodmFsdWUgdGhhdCBsZWFkcyB0byB0aGUgZ3JlYXRlciBudW1iZXIgb2YgY2x1c3RlcnMpOwogIAogICogX2NsdXN0LnBlYWtzLm1pbl86IG1pbmltdW0gbnVtYmVyIG9mIHZhcmlhYmxlcyBpbiBlYWNoIGNsdXN0ZXIuIE9ubHkgdGhlIGNsdXN0ZXJzIHdpdGggYXQgbGVhc3QgX2NsdXN0LnBlYWtzLm1pbl8gdmFyaWFibGVzIHdpbGwgYmUgY29uc2lkZXJlZC4KCiAgKiBfY2x1c3QublRvcF86IG51bWJlciBvZiB0b3AgbWV0YWJvbGl0ZXMgd2l0aCBncmVhdGVyIHNjb3JlIHRvIHNob3cgZm9yIGVhY2ggY2x1c3RlcjsKCiAgKiBfY2x1c3QubWF4UGVha3NfOiBtYXhpbXVtIG51bWJlciBvZiBwZWFrcyB0aGF0IGEgY2x1c3RlciBjYW4gaGF2ZSwgd2hpbGUgc2VhcmNoaW5nIGZvciB0aGUgYmVzdCBjb3JyZWxhdGlvbiB2YWx1ZS4gRGVmYXVsdHMgdG8gNDAsIHRoZSBvcmlnaW5hbCB2YWx1ZSB3aGVyZSB0aGUgY29kZSB3YXMgYWRhcGF0ZWQgZnJvbS4gQ2FuIGFsc28gYmUgTlVMTCBhbmQgdGhpcyB2YWx1ZSB3aWxsIGJlIHRoZSBudW1iZXIgb2YgcGVha3Mgb2YgdGhlIGxhcmdlciBjbHVzdGVyLgoKICAqIF9mcmVxXzogZnJlcXVlbmN5IG9mIHJlZmVyZW5jZSBzcGVjdHJhLCBpbiBIei4gTXVzdCBlaXRoZXIgYmUgNDAwLCA1MDAgb3IgNjAwLgoKICAqIF9udWNsXzogYXRvbWljIG51Y2xlaSBvZiByZWZlcmVuY2Ugc3BlY3RyYSwgZWl0aGVyICIxSCIgb3IgIjEzQyIuCgogICogX3NvbHZfOiBzb2x2ZW50IG9mIHRoZSBzYW1wbGUgdXNlZCB0byBvbnRhaW4gdGhlIHJlZmVyZW5jZSBzcGVjdHJhLiBQb3NzYmlsZSB2YWx1ZXM6ICIxMDAlX0RNU08iLCAiNSVfRE1TTyIsICJhY2V0b25lK0RNU08rdGV0cmFtZXRoeWx1cmVhIiwgImFjZXRvbmUrRE1TTyt0ZXRyYW1ldGh5bHVyZWEiLCAiQyIsICJDQ2w0IiwgIkNEM09EIiwgIkNEQ2wzIiwgImN5Y2xvaGV4YW5lIiwgIkQyTyIsICJETVNPLWQ2IiwgIkRNU08tZDYrSENsIiwgIm5lYXQiLCAiVE1TIiwgIldhdGVyIi4gX19vcHRpb25hbF9fCgogICogX3BIXzogcEggb2YgdGhlIHNhbXBsZSB1c2VkIHRvIG9udGFpbiB0aGUgcmVmZXJlbmNlIHNwZWN0cmEuIENhbiBiZSBhIG51bWJlciBjb3JyZXNwb25kaW5nIHRvIHRoZSBzYW1wbGUncyBwSCBvciBhIHZlY3RvciBvZiBsZW5ndGggdHdvIGluZGljYXRpbmcgYSBwSCBpbnRlcnZhbC4gX19vcHRpb25hbF9fCgogICogX3RlbXBfOiB0ZW1wZXJhdHVyZSBvZiB0aGUgc2FtcGxlIHVzZWQgdG8gb250YWluIHRoZSByZWZlcmVuY2Ugc3BlY3RyYSwgaW4gQ2Vsc2l1cy4gTXVzdCBlaXRoZXIgYmUgMjUgb3IgNTAuIF9fb3B0aW9uYWxfXwogIAoKIyMjIEV4YW1wbGUgPGEgbmFtZT0ibm1yX3BlYWtzX0lEX2V4Ij48L2E+CgpFeGFtcGxlIG1ha2VzIHVzZSBvZiB0aGlzIFtOTVIgcGVha3MgZGF0YXNldF0oI3JlYWRfbm1yX3BlYWtzX2V4KS4KCl9fMS5fXyBGaXJzdGx5LCB0byBwZXJmb3JtIG1ldGFib2xpdGUgaWRlbnRpZmljYXRpb24sIHRoZSBmb2xsb3dpbmcgcHJlLXByb2Nlc3NpbmcgdGFza3Mgd2VyZSBhcHBsaWVkIHRvIHRoZSBkYXRhc2V0LCBzYXZpbmcgdGhlIHByb2Nlc3NlZCBkYXRhIGluIGEgbmV3IGRhdGFzZXQgKG5tcl9wZWFrc19kYXRhc2V0X3Byb2Nlc3NlZF9JRCkKCiAgKiBUcmVhdG1lbnQgb2YgbWlzc2luZyB2YWx1ZXMsIGFzIHRoZSBkYXRhc2V0IGNvbnRhaW5zIG1pc3NpbmcgdmFsdWVzLCBieSByZXBsYWNpbmcgdGhlbSB3aXRoIHRoZSB2YWx1ZSA1KjEwXi00XjoKICAKYGBge3J9CmNvdW50X21pc3NpbmdfdmFsdWVzKG5tcl9wZWFrc19kYXRhc2V0KQpubXJfcGVha3NfZGF0YXNldF9wcm9jZXNzZWRfSUQ9bWlzc2luZ3ZhbHVlc19pbXB1dGF0aW9uKG5tcl9wZWFrc19kYXRhc2V0KQpgYGAKCiAgKiBMb2dhcml0bWljIHRyYW5zZm9ybWF0aW9uOgogIApgYGB7cn0Kbm1yX3BlYWtzX2RhdGFzZXRfcHJvY2Vzc2VkX0lEPXRyYW5zZm9ybV9kYXRhKG5tcl9wZWFrc19kYXRhc2V0X3Byb2Nlc3NlZF9JRCkKYGBgCgogICogQXV0byBzY2FsZToKICAKYGBge3J9Cm5tcl9wZWFrc19kYXRhc2V0X3Byb2Nlc3NlZF9JRD1zY2FsaW5nKG5tcl9wZWFrc19kYXRhc2V0X3Byb2Nlc3NlZF9JRCkKYGBgCgoKX18yLl9fIE5vdywgbWV0YWJvbGl0ZXMgd2lsbCBiZSBpZGVudGlmaWVkCgpgYGB7cn0Kbm1yX0lEX3JlcyA9IG5tcl9pZGVudGlmaWNhdGlvbihubXJfcGVha3NfZGF0YXNldF9wcm9jZXNzZWRfSUQsIGNsdXN0Lm1heFBlYWtzID0gTlVMTCwgY2x1c3QublRvcCA9IDEwKQpgYGAKCl9fMy5fXyBFeGFtcGxlIG9mIHJlc3VsdHMgdGhhdCBjYW4gYmUgb2J0YWluZWQgZm9yIGVhY2ggY2x1c3RlciBmb3JtZWQ6CgogICogUGVha3Mgb2YgdGhlIGNsdXN0ZXI6CgpgYGB7cn0Kbm1yX0lEX3JlcyRDbHVzdGVyMSRjbHVzdGVyLnBlYWtzWywxXQpgYGAKCiAgKiBTdW1tYXJ5IG9mIHRoZSB0b3AgbWV0YWJvbGl0ZXMgaWRlbnRpZmllZCBmb3IgdGhhdCBjbHVzdGVyLCB3aXRoIHRoZSByZXNwZWN0aXZlIHNjb3JlczoKICAKYGBge3J9CmtuaXRyOjprYWJsZShubXJfSURfcmVzJENsdXN0ZXIxJHN1bW1hcnksIGNvbC5uYW1lcz0iSmFjY2FyZCBJbmRleCIpCmBgYAoKX180Ll9fIEZ1cnRoZXIgcmVzdWx0cyBmb3IgZWFjaCBtZXRhYm9saXRlIG1hdGNoZWQ6CiAgCiAgKyBTY29yZSAoamFjY2FyZCBpbmRleCkKICAgICAgCmBgYHtyfQpubXJfSURfcmVzJENsdXN0ZXIxJG1ldGFib2xpdGVzLm1hdGNoZWRbWzFdXSRzY29yZQpgYGAKCiAgKyBQZWFrcyBvZiB0aGUgcmVmZXJlbmNlIHNwZWN0cnVtIHRoYXQgbWF0Y2hlZCB0aGUgY2x1c3RlcgoKYGBge3J9Cm5tcl9JRF9yZXMkQ2x1c3RlcjEkbWV0YWJvbGl0ZXMubWF0Y2hlZFtbMV1dJG1hdGNoZWRfcGVha3NfcmVmCmBgYAoKICArIFBlYWtzIG9mIHRoZSBjbHVzdGVyIHRoYXQgbWF0Y2hlZCB0aGUgcmVmZXJlbmNlIHNwZWN0cnVtCiAgICAgIApgYGB7cn0Kbm1yX0lEX3JlcyRDbHVzdGVyMSRtZXRhYm9saXRlcy5tYXRjaGVkW1sxXV0kbWF0Y2hlZF9wZWFrc19jbHVzdApgYGAKCiAgKyBQZWFrcyBvZiB0aGUgcmVmZXJlbmNlIHNwZWN0cmEKICAgICAgCmBgYHtyfQpubXJfSURfcmVzJENsdXN0ZXIxJG1ldGFib2xpdGVzLm1hdGNoZWRbWzFdXSRyZWZlcmVuY2VfcGVha3MKYGBgCgoKIyBQYXRod2F5IEFuYWx5c2lzCgojIyBGdW5jdGlvbnMgdG8gdXNlCgpfXzEuX18gR2V0IG9ubHkgdGhlIHBhdGhzIG9mIHRoZSBtZW50aW9uZWQgb3JnYW5pc20gdGhhdCBjb250YWluIG9uZSBvciBtb3JlIG9mIHRoZSBnaXZlbiBjb21wb3VuZHMKCj4gZ2V0X3BhdGhzX3dpdGhfY3Bkc19vcmcob3JnYW5pc21fY29kZSwgY29tcG91bmRzLCBmdWxsLnJlc3VsdD1UKQoKICAqIF9vcmdhbmlzbVxfY29kZV86IE9yZ2FuaXNtIGNvZGUuIFRoZSBjb3JyZWN0IGNvZGUgZm9yIGFuIG9yZ2FuaXNtIGNhbiBiZSBjb25zdWx0ZWQgdXNpbmcgZnVuY3Rpb24gX2dldFxfT3JnYW5pc21zQ29kZXNfLCBtZW50aW9uZWQgaW4gdGhlIGFkZGl0aW9uYWwgZnVuY3Rpb25zIGJlbG93OwogIAogICogX2NvbXBvdW5kc186IE5hbWVkIHZlY3RvciB3aXRoIGtlZ2cgY29kZXMgb2YgY29tcG91bmRzIGFuZCByZXNwZWN0aXZlIG5hbWVzLiBUaGlzIHZlY3RvciBjYW4gYmUgb2J0YWluZWQgYnkgdXNpbmcgdGhlIGZ1bmN0aW9uIF9nZXRcX2NwZFxfbmFtZXNfIG9yIHRoZSBmdW5jdGlvbiBfY29udmVydFxfaG1kYlxfdG9cX2tlZ2dfLCBtZW50aW9uZWQgaW4gdGhlIGFkZGl0aW9uYWwgZnVuY3Rpb25zIGJlbG93OwogIAogICogX2Z1bGwucmVzdWx0XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiBhIGZ1bGwgcmVzdWx0IGlzIHRvIGJlIGdpdmVuLiBEZWZhdWx0cyB0byBUUlVFLgoKSWYgZnVsbCByZXN1bHQgaXMgY2hvc2VuLCB0aGUgZGF0YSBmcmFtZSByZXR1cm5lZCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiB0aGUgcGF0aHdheXMgb2YgdGhlIG9yZ2FuaXNtIHRoYXQgY29udGFpbnMgb25lIG9yIG1vcmUgb2YgdGhlIGdpdmVuIGNvbXBvdW5kcyBhbmQsIGZvciBlYWNoIHBhdGh3YXksIHRoZSBrZWdnIGNvZGVzIChhbmQgdGhlaXIgbmFtZXMpIG9mIHRoZSBjb21wb3VuZHMgZ2l2ZW4gdGhhdCBhcmUgcHJlc2VudCBpbiB0aGF0IHBhdGguCgpJZiBmdWxsIHJlc3VsdCBpcyBub3Qgd2FudGVkLCBvbmx5IHRoZSBwYXRod2F5cyB3aWxsIGJlIGdpdmVuLgoKCl9fMi5fXyBDcmVhdGUgdGhlIG1ldGFib2xpYyBwYXRod2F5IHdhbnRlZC4KClRoZSBwYXRod2F5IGNyZWF0ZWQgY29udGFpbnMgdGhlIGNvbXBvdW5kcywgcmVhY3Rpb25zIGFuZCBvdGhlciBwYXRocyB0aGF0IGl0IGNvbm5lY3RzIHRvIGFzIG5vZGVzLgoKVGhlIGNvbXBvdW5kcyBnaXZlbiBpbiBjb21wb3VuZHMgYXJlIGNvbG9yZWQgaW4gYmx1ZSwgd2hpbGUgdGhlIHJlc3Qgb2YgdGhlIGNvbXBvdW5kcyBhcmUgY29sb3JlZCBpbiBncmV5LgoKVGhlIG90aGVyIHBhdGhzIHRoYXQgaXQgbWF5IGNvbm5lY3QgdG8gYXJlIGNvbG9yZWQgaW4gb3JhbmdlLgoKUmV2ZXJzaXZlIHJlYWN0aW9ucyBhcmUgY29sb3JlZCBpbiBncmVlbiBhbmQgdGhlIGlycmV2ZXJzaWJsZSBvbmVzIGluIHJlZC4KCj4gcGF0aHdheV9hbmFseXNpcyhjb21wb3VuZHMsIHBhdGh3YXksIG5vZGVOYW1lcz0ia2VnZyIsIG5vZGVUb29sdGlwPUYsIG1hcC56b29tPUYsIG1hcC5sYXlvdXQ9InByZXNldCIsIG1hcC53aWR0aD1OVUxMLCBtYXAuaGVpZ2h0PU5VTEwpCgogICogX2NvbXBvdW5kc186IHZlY3RvciBvZiBjb21wb3VuZHMgb2YgaW50ZXJlc3Q7CiAgCiAgKiBfcGF0aHdheV86IEtFR0cgY29kZSAoZS5nLiwgImhzYTAwMDEwIikgb2YgdGhlIHBhdGggd2FudGVkOwogIAogICogX25vZGVOYW1lc186IGhvdyB0aGUgbm9kZXMgc2hvdWxkIGJlIG5hbWVkLiBJZiAia2VnZyIsIG5vZGVzIGFyZSBuYW1lZCB3aXRoIGtlZ2cgY29kZXMuIElmICJuYW1lcyIsIG5vZGVzIGFyZSBuYW1lZCB3aXRoIHRoZSBjb21tb24gbmFtZXMuCiAgCiAgKiBfbm9kZVRvb2x0aXBfOiBpZiBhIHRvb2x0aXAgc2hvdWxkIGFwcGVhciB3aGVuIGhvdmVyaW5nIGEgbm9kZS4gT25seSB3b3JrcyBpbiBjZXJ0YWluIGVudmlyb25tZW50czsKICAKICAqIF9tYXAuem9vbV86IGlmIHRoZSBtYXAgc2hvdWxkIGhhdmUgdGhlIHpvb20gaW4gYW5kIG91dCBvcHRpb24uIE9ubHkgd29ya3MgaW4gY2VydGFpbiBlbnZpcm9ubWVudHM7CiAgCiAgKiBfbWFwLmxheW91dF86IGxheW91dCBvZiB0aGUgbWFwLCBhdmFpbGFibGUgdmFsdWVzIGFyZSB0aGUgb25lcyBvZiBjeXRvc2NhcGUgKCJicmVhZHRoZmlyc3QiLCAicHJlc2V0IiwgImNvc2UiLCAuLi4pOwoKICAqIF9tYXAud2lkdGhfOiB3aWR0aCBvZiB0aGUgbWFwLCBpbiBwZXJjZW50YWdlOwogIAogICogX21hcC5oZWlnaHRfOiBIZWlnaHQgb2YgdGhlIG1hcCwgaW4gcHggKGUuZy4gIjUwMHB4IikuCgojIyBBZGRpdGlvbmFsIGZ1bmN0aW9ucwoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiByZXR1cm5zIGEgZGF0YWZyYW1lIHdpdGggdGhlIHQtbnVtYmVyLCBvcmdhbmlzbSBrZWdnIGNvZGUsIGZ1bGwgbmFtZSBhbmQgcGh5bG9nZW55IGZvciBlYWNoIGtlZ2cgb3JnYW5pc206Cgo+IGdldF9PcmdhbmlzbXNDb2RlcygpCgpUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIHJldHVybnMgYSBuYW1lZCB2ZWN0b3Igd2l0aCB0aGUgbmFtZXMgb2YgdGhlIGNvbXBvdW5kcy4gVGhlIG5hbWVzIG9mIHRoZSB2ZWN0b3IgYXJlIHRoZSBjb21wb3VuZHMnIG5hbWVzIGFuZCB0aGUgdmVjdG9yIGVsZW1lbnRzIHRoZSBrZWdnIGNvZGVzLgoKPiBnZXRfY3Bkc19uYW1lcyhrZWdnX2NvZGVzKQoKICAqIF9rZWdnXF9jb2Rlc186IENoYXJhY3RlciB2ZWN0b3Igd2l0aCBrZWdnIGNvZGVzLgoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiByZXR1cm4gYSBuYW1lZCB2ZWN0b3Igd2l0aCBrZWdnIGNvZGVzIGFuZCByZXNwZWN0aXZlIG1ldGFib2xpdGUgbmFtZXMuIFZlY3RvciBuYW1lcyBhcmUgdGhlIGNvbXBvdW5kIG5hbWVzIGFuZCB0aGUgdmVjdG9yIGVsZW1lbnRzIHRoZSBrZWdnIGNvZGVzOgoKPiBjb252ZXJ0X2htZGJfdG9fa2VnZyhobWRiX2NvZGVzKQoKICAqIF9obWRiXF9jb2Rlc186VmVjdG9yIHdpdGggdGhlIEhNREIgY29kZXMgKGVhY2ggaG1kYiBjb2RlIG11c3QgaGF2ZSA3IGRpZ2l0cywgZS5nLiwgSE1EQjAwMDAwMDEpLgogIApUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIHJldHVybnMgdGhlIG1ldGFib2xpYyBrZWdnIHBhdGhzIHByZXNlbnQgaW4gbWVudGlvbmVkIG9yZ2FuaXNtOgoKPiBnZXRfTWV0YWJQYXRoc19vcmcob3JnX2NvZGUpCgogICogX29yZ1xfY29kZV86IE9yZ2FuaXNtIGNvZGUuIFRoZSBjb3JyZWN0IGNvZGUgZm9yIGFuIG9yZ2FuaXNtIGNhbiBiZSBjb25zdWx0ZWQgdXNpbmcgZnVuY3Rpb24gX2dldFxfT3JnYW5pc21zQ29kZXNfLCBtZW50aW9uZWQgYWJvdmUuCgoKIyMgRXhhbXBsZQoKVGhpcyBleGFtcGxlIHVzZXMgdGhlIHJlc3VsdHMgb2J0YWluZWQgaW4gdGhlIGV4YW1wbGUgZm9yIGlkZW50aWZpY2F0aW9uIG9mIG1ldGFib2xpdGVzIGZyb20gTk1SIHBlYWtzIChbaGVyZV0obm1yX3BlYWtzX0lEX2V4KSkuCgpfXzEuX18gR2V0IGlkZW50aWZpZWQgbWV0YWJvbGl0ZXMgaW4gb25lIHZlY3RvcjoKCmBgYHtyfQppZF9tZXRhYnM9YygpCmZvciAoY2x1c3RlciBpbiBubXJfSURfcmVzKXsKICBpZF9tZXRhYnM9YyhpZF9tZXRhYnMsIG5hbWVzKGNsdXN0ZXIkc3VtbWFyeSkpCn0KaWRfbWV0YWJzCmBgYAoKX18yLl9fIENvbnZlcnQgbWV0YWJvbGl0ZXMgZnJvbSBITURCIGNvZGVzIHRvIEtFR0cgY29kZXM6CgpgYGB7cn0KaWRfbWV0YWJzX2tlZ2c9Y29udmVydF9obWRiX3RvX2tlZ2coaWRfbWV0YWJzKQojaWRfbWV0YWJzX2tlZ2cKa25pdHI6OmthYmxlKGNiaW5kKG5hbWVzKGlkX21ldGFic19rZWdnKSwgaWRfbWV0YWJzX2tlZ2cpLCByb3cubmFtZXM9RiwgY29sLm5hbWVzPWMoIk5hbWVzIiwgIktFR0cgY29kZXMiKSkKYGBgCgpfXzMuX18gR2V0IHRoZSBtZXRhYm9saWMgcGF0aHMgdGhhdCBoYXZlIG9uZSBvcmUgbW9yZSBvZiB0aGUgaWRlbnRpZmllZCBjb21wb3VuZHMsIG9idGFpbmluZyB0aGUgZnVsbCByZXN1bHRzCgpgYGB7cn0KY3Bkc19wYXRocz1zcGVjbWluZTo6Z2V0X3BhdGhzX3dpdGhfY3Bkc19vcmcoImF0aCIsIGlkX21ldGFic19rZWdnKQprbml0cjo6a2FibGUoY3Bkc19wYXRocykKYGBgCgpfXzQuX18gVmlzdWFsaXphdGlvbiBvZiBvbmUgb2YgdGhlIHBhdGh3YXlzIG9idGFpbmVkIGluIHRoZSBwcmV2aW91cyBwb2ludCAoVml0YW1pbiBCNikKCmBgYHtyIGV2YWw9Rn0KcGF0aHdheV9hbmFseXNpcyhpZF9tZXRhYnNfa2VnZywgImF0aDAwNzUwIiwgbm9kZU5hbWVzPSJuYW1lcyIsIG1hcC56b29tPVQsIG5vZGVUb29sdGlwPVQpCmBgYAoKIVtdKC9ob21lL3NjYXJkb3NvL0RvY3VtZW50cy9zcGVjbWluZS9yZXBvcnRzL3ZpdGFtaW5iNi5wbmcpCgoKCiMgT3RoZXIgRnVuY3Rpb25zCgpfX1lvdSBjYW4gY29udmVydCB0aGUgYWJzb3JiYW5jZSB2YWx1ZXMgb2YgdGhlIGRhdGFzZXQgaW50byB0cmFuc21pdHRhbmNlIHZhbHVlcywgYW5kIHZpY2UgdmVyc2FfXwoKPiBhYnNvcmJhbmNlX3RvX3RyYW5zbWl0dGFuY2UoZGF0YXNldCkKCj4gdHJhbnNtaXR0YW5jZV90b19hYnNvcmJhbmNlKGRhdGFzZXQsIHBlcmNlbnQgPSBUKQoKX19Zb3UgY2FuIGNvdW50IHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgb24gdGhlIHdob2xlIGRhdGFzZXQsIGFuZCBwZXIgc2FtcGxlIG9yIHZhcmlhYmxlX18KCj4gY291bnRfbWlzc2luZ192YWx1ZXMoZGF0YXNldCkKCj4gY291bnRfbWlzc2luZ192YWx1ZXNfcGVyX3NhbXBsZShkYXRhc2V0KQoKPiBjb3VudF9taXNzaW5nX3ZhbHVlc19wZXJfdmFyaWFibGUoZGF0YXNldCkKCgpfX1lvdSBjYW4gZ2V0IHRoZSBkYXRhIG1hdHJpeCBvbiB0aGUgZGF0YXNldF9fCgo+IGdldF9kYXRhKGRhdGFzZXQpCgpfX1lvdSBjYW4gZ2V0IHRoZSBkYXRhIG9uIHRoZSBkYXRhc2V0IGFzIGEgZGF0YSBmcmFtZV9fCgo+IGdldF9kYXRhX2FzX2RmKGRhdGFzZXQpCgpfX1lvdSBjYW4gZ2V0IHRoZSB2YWx1ZXMgb2YgYSB2YXJpYWJsZSBpbiBhIHNhbXBsZV9fCgo+IGdldF9kYXRhX3ZhbHVlKGRhdGFzZXQsIHguYXhpcy52YWwsIHNhbXBsZSwgYnkuaW5kZXggPSBGKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3guYXhpcy52YWxfOiBzdHJpbmcgaW5kaWNhdGluZyB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUgb3IgbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIHRoZSBwb3NpdGlvbiAoaW5kZXgpIG9mIHRoZSB2YXJpYWJsZSBpbiB0aGUgZGF0YTsKICAKICAqIF9zYW1wbGVfOiBzdHJpbmcgaW5kaWNhdGluZyB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlIG9yIG51bWVyaWMgdmFsdWUgaW5kaWNhdGluZyB0aGUgcG9zaXRpb24gKGluZGV4KSBvZiB0aGUgc2FtcGxlIGluIHRoZSBkYXRhOwogIAogICogX2J5LmluZGV4XzogYm9vbGVhbiB2YWx1ZSAoVFJVRSBvciBGQUxTRSkgaW5kaWNhdGluZyBpZiBpbiB0aGUgYXJndW1lbnRzIF94LmF4aXMudmFsXyBhbmQgX3NhbXBsZV8gaXMgZ2l2ZW4gdGhlIGluZGV4IChieS5pbmRleD1UUlVFKSBvciB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUgYW5kIHNhbXBsZSAoYnkuaW5kZXg9RkFMU0UpLiBEZWZhdWx0cyB0byBGQUxTRS4KCl9fWW91IGNhbiBhbGwgc2FtcGxlcycgdmFsdWVzIGluIHRoZSBkYXRhc2V0IGZvciBhIGNlcnRhaW4gdmFyaWFibGVfXwoKPiBnZXRfZGF0YV92YWx1ZXMoZGF0YXNldCwgeC5heGlzLnZhbCwgYnkuaW5kZXggPSBGQUxTRSkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF94LmF4aXMudmFsXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIG9yIG51bWVyaWMgdmFsdWUgaW5kaWNhdGluZyB0aGUgcG9zaXRpb24gKGluZGV4KSBvZiB0aGUgdmFyaWFibGUgaW4gdGhlIGRhdGE7CiAgCiAgKiBfYnkuaW5kZXhfOiBib29sZWFuIHZhbHVlIChUUlVFIG9yIEZBTFNFKSBpbmRpY2F0aW5nIGlmIGluIHRoZSBhcmd1bWVudCBfeC5heGlzLnZhbF8gaXMgZ2l2ZW4gdGhlIGluZGV4IChieS5pbmRleD1UUlVFKSBvciB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUgKGJ5LmluZGV4PUZBTFNFKS4gRGVmYXVsdHMgdG8gRkFMU0UuCgpfX1lvdSBjYW4gZ2V0IGEgZGF0YSBmcmFtZSBvZiB0aGUgbWV0YWRhdGEgb2YgdGhlIGRhdGFzZXRfXwoKPiBnZXRfbWV0YWRhdGEoZGF0YXNldCkKCl9fWW91IGNhbiBnZXQgdGhlIHZhbHVlIG9mIGEgbWV0YWRhdGEgdmFyaWFibGUgaW4gYSBzYW1wbGVfXwoKPiBnZXRfbWV0YWRhdGFfdmFsdWUoZGF0YXNldCwgdmFyaWFibGUsIHNhbXBsZSkKCiAgKiBfZGF0YXNldF86IGEgc3BlY21pbmUgZGF0YXNldDsKICAKICAqIF92YXJpYWJsZV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBuYW1lIG9mIHRoZSBtZXRhZGF0YSB2YXJpYWJsZSBvciBudW1lcmljIHZhbHVlIGluZGljYXRpbmcgdGhlIHBvc2l0aW9uIChpbmRleCkgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlOwogIAogICogX3NhbXBsZV86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBuYW1lIG9mIHRoZSBzYW1wbGUgb3IgbnVtZXJpYyB2YWx1ZSBpbmRpY2F0aW5nIHRoZSBwb3NpdGlvbiAoaW5kZXgpIG9mIHRoZSBzYW1wbGU7CgpfX1lvdSBjYW4gZ2V0IHRoZSBzYW1wbGVzJyB2YWx1ZXMgb2YgYSBtZXRhZGF0YSB2YXJpYWJsZV9fCgo+IGdldF9tZXRhZGF0YV92YXIoZGF0YXNldCwgdmFyKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX3ZhcmlhYmxlXzogc3RyaW5nIGluZGljYXRpbmcgdGhlIG5hbWUgb2YgdGhlIG1ldGFkYXRhIHZhcmlhYmxlIG9yIG51bWVyaWMgdmFsdWUgaW5kaWNhdGluZyB0aGUgcG9zaXRpb24gKGluZGV4KSBvZiB0aGUgbWV0YWRhdGEgdmFyaWFibGUuCgpfX1lvdSBjYW4gZ2V0IHRoZSBuYW1lcyBvZiB0aGUgc2FtcGxlcyBvbiB0aGUgZGF0YXNldF9fCgo+IGdldF9zYW1wbGVfbmFtZXMoZGF0YXNldCkKCgpfX1lvdSBjYW4gZ2V0IHRoZSB0eXBlIG9mIGRhdGFzZXRfXwoKPiBnZXRfdHlwZShkYXRhc2V0KQoKX19Zb3UgY2FuIGdldCB0aGUgZGF0YSB4IHZhbHVlcyBhcyBudW1iZXJzIGluc3RlYWQgb2Ygc3RyaW5ncy4gT25seSBwb3NzaWJsZSBpZiBub3QgY29uY2VudHJhdGlvbnMgZGF0YV9fCgo+IGdldF94X3ZhbHVlc19hc19udW0oZGF0YXNldCkKCl9fWW91IGNhbiBnZXQgdGhlIGRhdGEgeCB2YWx1ZXMgYXMgc3RyaW5nc19fCgo+IGdldF94X3ZhbHVlc19hc190ZXh0KGRhdGFzZXQpCgpfX1lvdSBnZXQga25vdyBpZiB0aGUgZGF0YXNldCBpbiBxdWVzdGlvbiBpcyBzcGVjdHJhbCBvciBub3RfXwoKPiBpc19zcGVjdHJhKGRhdGFzZXQpCgpfX1lvdSBjYW4gdXBkYXRlIHRoZSBtZXRhZGF0YSBpbiB0aGUgZGF0YXNldF9fCgo+IHNldF9tZXRhZGF0YShkYXRhc2V0LCBuZXcubWV0YWRhdGEpCgogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbmV3Lm1ldGFkYXRhXzogbWF0cml4IG9yIGRhdGFmcmFtZSBvZiB0aGUgbmV3IG1ldGFkYXRhLgoKX19Zb3UgY2FuIGdpdmUgbmV3IG5hbWVzIHRvIHRoZSBzYW1wbGVzIGluIHRoZSBkYXRhc2V0X18KCj4gc2V0X3NhbXBsZV9uYW1lcyhkYXRhc2V0LCBuZXcuc2FtcGxlLm5hbWVzKQogIAogICogX2RhdGFzZXRfOiBhIHNwZWNtaW5lIGRhdGFzZXQ7CiAgCiAgKiBfbmV3LnNhbXBsZXMubmFtZXNfOiBjaGFyYWN0ZXIgdmVjdG9yIHdpdGggdGhlIG5ldyBuYW1lcyBmb3IgdGhlIHNhbXBsZXMuCgpfX1lvdSBjYW4gc2V0IGEgbmV3IGxhYmVsIHRvIHRoZSB5eSBheGlzX18KCj4gc2V0X3ZhbHVlX2xhYmVsKGRhdGFzZXQsIG5ldy52YWwubGFiZWwpCgpfX1lvdSBjYW4gc2V0IGEgbmV3IGxhYmVsIHRvIHRoZSB4eCBheGlzX18KCj4gc2V0X3hfbGFiZWwoZGF0YXNldCwgbmV3LngubGFiZWwpCgpfX1lvdSBjYW4gZ2l2ZSBuZXcgdmFsdWVzIHRvIHRoZSB4eCBheGlzX18KCj4gc2V0X3hfdmFsdWVzKGRhdGFzZXQsIG5ldy54LnZhbHVlcywgbmV3LngubGFiZWwgPSBOVUxMKQoKICAqIF9kYXRhc2V0XzogYSBzcGVjbWluZSBkYXRhc2V0OwogIAogICogX25ldy54LnZhbHVlc186IG51bWVyaWMgb3IgY2hhcmFjdGVyIHZlY3RvciB3aXRoIHRoZSBuZXcgdmFsdWVzIGZvciB0aGUgeHggYXhpczsKICAKICAqIF9uZXcueC5sYWJlbF86IHN0cmluZyBpbmRpY2F0aW5nIHRoZSBuZXcgbGFiZWwgZm9yIHRoZSB4eCBheGlzLiBfX29wdGlvbmFsX18KCgoKCiMgUHJvYmxlbXMgcnVubmluZyBjb2RlOiBzb2x1dGlvbnMgPGEgbmFtZT0icHJvYmxlbXNfc29sdXRpb25zIj48L2E+CgojIyAiTWF4aW11bSBudW1iZXIgb2YgRExMcyByZWFjaGVkIC4uLiIKCklmIHlvdSByZWNlaXZlIHRoaXMgZXJyb3IgbWVzc2FnZSwgeW91IGhhdmUgdG8gaW5jcmVhc2UgdGhlIG1heGltdW0gbnVtYmVyIG9mIERMTHMgUiBjYW4gbG9hZCBieSBzZXR0aW5nIHRoZSB2YXJpYWJsZSBSX01BWF9OVU1fRExMUy4KCl9fMS5fXyBGaW5kIG91dCB3aGVyZSB0aGUgUmVudmlyb24gb3IgUmVudmlyb24uc2l0ZSBmaWxlcyBhcmUgbG9jYXRlZCBpbiB5b3VyIG1hY2hpbmU6CgpgYGAge3IgZXZhbD1GfQpkaXIoU3lzLmdldGVudigiUl9IT01FIikscmVjdXJzaXZlID0gVCxmdWxsLm5hbWVzID0gVCxwYXR0ZXJuID0gIlJlbnZpcm9uIikKYGBgCgpfXzIuX18gQWRkIHRoZSBjb21tYW5kIF9SX01BWF9OVU1fRExMUz0xNTBfIGF0IHRoZSBlbmQgb2Ygb25lIG9mIHRoZXNlIGZpbGVzOgoKICAqIEV4YW1wbGUgZm9yIE1BQ09TOgoKYGBge3IgZXZhbD1GfQpzeXN0ZW0oJyBlY2hvICJSX01BWF9OVU1fRExMUz0xNTAiID4+IC9MaWJyYXJ5L0ZyYW1ld29ya3MvUi5mcmFtZXdvcmsvUmVzb3VyY2VzL2V0Yy9SZW52aXJvbicpCmBgYAoKICAqIEV4YW1wbGUgZm9yIFVCVU5UVToKICAKX0NvbW1hbmQgaGFzIHRvIGJlIHJ1biBhcyBhZG1pbmlzdHJhdG9yLiBTbyBydW4gciBvbiB0ZXJtaW5hbCAodHlwZSBfc3VkbyBSXyksIGluc2VydCBwYXNzd29yZCBhbmQgdHlwZTpfCgpgYGB7ciBldmFsPUZ9CnN5c3RlbSgnIGVjaG8gIlJfTUFYX05VTV9ETExTPTE1MCIgPj4gL3Vzci9saWIvUi9ldGMvUmVudmlyb24nKQpgYGAKCgojIyBJZiB5b3UgYXJlIGdldHRpbmcgYSBwcm9ibGVtIHdpdGggX3JnbF8gcGFja2FnZQoKX19JZiB5b3UgYXJlIG9uIGxpbnV4IGFuZCBoYXZpbmcgcHJvYmxlbXMgdXNpbmcgY2VydGFpbiBmdW5jdGlvbnMgZHVlIHRvIF9yZ2xfIHBhY2thZ2UgKCJfb2JqZWN0ICdyZ2xfY2xlYXInIG5vdCBmb3VuZF8iLCBmb3IgZXhhbXBsZSksIHRoZSBwcm9ibGVtIG1heSBiZSB0aGUgdmVyc2lvbiBvZiB0aGUgX3JnbF8gcGFja2FnZSB5b3UgaGF2ZS5fXwoKU28sIHBlcmZvcm1pbmcgdGhlIGZvbGxvd2luZyBpbiB0aGUgdGVybWluYWwgbWF5IGJlIG5lY2Vzc2FyeSB0byB1c2UgdGhlc2UgZnVuY3Rpb25zOgoKYHN1ZG8gYXB0LWdldCBpbnN0YWxsIGxpYmdsdTEtbWVzYS1kZXZgCgpBZnRlciB0aGlzLCByZWluc3RhbGwgdGhlIF9yZ2xfIHBhY2thZ2UKCmBgYHtyIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcygicmdsIikKYGBgCgoKIyMgX0Vycm9yIGluIG12clZhbHN0YXRzKG9iamVjdCA9IG9iamVjdCwgZXN0aW1hdGUgPSAidHJhaW4iKSA6IGNvdWxkIG5vdCBmaW5kIGZ1bmN0aW9uICJtdnJWYWxzdGF0cyJfCgpfX1doZW4gcnVuaW5nIHRoZSBmdW5jdGlvbiBfdHJhaW5cX21vZGVsc1xfcGVyZm9ybWFuY2VfLCB0aGUgYWJvdmUgZXJyb3IgbWF5IGFyaXNlLl9fCgpUd28gcG9zc2libGUgc29sdXRpb25zIGFyZToKCiogUmVpbnN0YWxsIF9jYXJldF8gcGFja2FnZSwgYXMgaXQgbWlnaHQgYmUgYmVjYXVzZSB5b3UgaGF2ZSBhbiBvbGRlciB2ZXJzaW9uIG9mIHRoaXMgcGFja2FnZSwgd2hlcmUgdGhlIGJ1ZyB3YXMgZml4ZWQgYWxyZWFkeTsKCmBgYHtyIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQpgYGAKCiogT3IsIGluIGNhc2UgdGhlIGVycm9yIHBlcnNpc3RzLCB5b3UgaGF2ZSB0byBsb2FkIHRoZSBfcGxzXyBwYWNrYWdlLCBhcyBhIG5ldyByZWxlYXNlIG9mIF9jYXJldF8gcGFja2FnZSB3aXRob3V0IHRoZSBidWcgd2FzIG5vdCB5ZXQgcHJvdmlkZWQuCgpgYGB7ciBldmFsPUZ9CmxpYnJhcnkocGxzKQpgYGAKCgo=